},
},
watch: {
- files: ['<%= jshint.files %>', '<%= concat.css.src %>', '<%= copy.main.files[0].src %>'],
+ files: ['<%= jshint.files %>', '<%= concat.css.src %>', '<%= copy.main.files[0].src %>', '<%= less.production.src %>'],
tasks: ['default']
},
bower_concat: {
}
}
},
+ less: {
+ production: {
+ src:["src/**/*.less", "src/**/*.css"],
+ dest: 'dist/<%= pkg.name %>.css',
+ }
+ },
concat: {
- css: {
- src: ['src/**/*.css'],
- dest: 'dist/<%= pkg.name %>.css'
- },
js: { // Wrap subtask
options: {
banner: "$(function(){\n",
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-concat-in-order');
grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
- grunt.registerTask('default', ['jshint', 'bower_concat', 'concat_in_order', 'concat', 'copy']);
+ grunt.registerTask('default', ['jshint', 'less', 'bower_concat', 'concat_in_order', 'concat', 'copy']);
};
body {
- background: #f9f9fa;
+ background: #f9f9fa;
}
-
-.text {
- font-size: 20pt;
- margin-top: 5px;
- margin-bottom: 5px;
+.todo > .text {
+ font-size: 20pt;
+ margin-top: 5px;
+ margin-bottom: 5px;
}
-
.bullets > .todo {
- margin-left: 30px;
+ margin-left: 30px;
}
-
.todo.completed > .text {
- text-decoration: line-through;
- color: grey;
+ text-decoration: line-through;
+ color: grey;
}
-
.todo.collapsed .todo {
- display: none;
+ display: none;
}
-
-#shortcuts .editing {
- color: red;
- text-color: red;
- border: 2px solid;
-}
-
#todo-list {
- width: 80%;
- margin: auto;
- max-width: 600px;
- border: 1px solid #ccc;
- padding-top: 60px;
- padding-bottom: 10px;
- background: #ffffff;
+ width: 80%;
+ margin: auto;
+ max-width: 600px;
+ border: 1px solid #ccc;
+ padding-top: 60px;
+ padding-bottom: 10px;
+ background: #ffffff;
+}
+.with-dropshadow {
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}
-
#shortcuts-wrapper {
- position: fixed;
- bottom: -2px;
- left: 10px;
- width: 200px;
- background: #ffffff;
+ position: fixed;
+ bottom: -2px;
+ left: 10px;
+ width: 200px;
+ background: #ffffff;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
+}
+#shortcuts-wrapper .title {
+ padding: 10px;
+ font-size: 100%;
+ border-bottom: 1px solid #979797;
+ font-weight: bold;
+ background: -webkit-gradient(linear, left bottom, left top, color-stop(1, #ededed), color-stop(0, #c4c4c4));
}
-
.shortcuts {
- border-collapse: collapse;
- box-shadow: 0 2px 12px rgba(0,0,0,0.6);
- display: block;
+ border-collapse: collapse;
+ display: block;
+ width: 100%;
}
-
.shortcuts .description {
- background-color: #efefef;
- max-width: 80px;
- padding: 0 0 0 7px;
+ background-color: #efefef;
+ max-width: 80px;
+ padding: 0 0 0 7px;
}
-
.shortcuts .keybinding {
- padding: 0 0 0 7px;
+ padding: 0 0 0 7px;
}
-
.shortcuts td {
- border-top: 0;
- border-bottom: 1px solid #aaa;
+ border-top: 0;
+ border-bottom: 1px solid #aaa;
}
-
-#shortcuts-wrapper .title {
- padding: 10px;
- font-size: 100%;
- border-bottom: 1px solid #979797
- font-weight: bold;
- background: -webkit-gradient( linear, left bottom, left top, color-stop(1, rgb(237,237,237)), color-stop(0, rgb(196,196,196)) );
+.shortcuts .editing {
+ color: red;
+ text-color: red;
}
-
#reset-button {
- position: fixed;
- bottom: 3px;
- right: 3px;
+ position: fixed;
+ bottom: 3px;
+ right: 3px;
}
<div id="todo-list">
</div>
- <button id="reset-button">Everyone heard me say "reset button", right?</button>
+ <button id="reset-button">Initial setup / Reset</button>
<div id="shortcuts-wrapper">
</div>
</div>
--- /dev/null
+v1.0.1:
+ date: 2015-04-07
+ changes:
+ - Fix backwards compat, lock to ~2.4
+v1.0.0:
+ date: 2014-12-23
+ changes:
+ - Update to less 2.1.0
+v0.12.0:
+ date: 2014-10-23
+ changes:
+ - Added cleancssOptions as a option.
+ - When possible, includes filename for failed writes.
+ - Ensure banner only outputs on the first file in a series.
+v0.11.4:
+ date: 2014-07-29
+ changes:
+ - Fix 'banner', 'urlArgs' LESS options
+ - Fixes npm 2 peerDependencies issues
+v0.11.3:
+ date: 2014-06-20
+ changes:
+ - Update to Less ~1.7.2.
+v0.11.2:
+ date: 2014-06-01
+ changes:
+ - Lock to less 1.7.0.
+v0.11.1:
+ date: 2014-05-26
+ changes:
+ - Fix `modifyVars` to work when less file ends with a comment.
+v0.11.0:
+ date: 2014-03-19
+ changes:
+ - Custom functions can return types defined by less
+ - paths option now accepts a function
+ - Replaced deprecated grunt.util methods
+ - Removes deprecated grunt.lib.contrib
+v0.10.0:
+ date: 2014-03-01
+ changes:
+ - sourceMapBasepath accepts a function.
+ - Update copyright to 2014.
+ - Update .gitattributes.
+ - Update less.js to v1.7.0.
+ - Prevent CRLF in the repo.
+ - Adds modify-vars option.
+ - Changed to async stack call.
+ - Fixes data-uri test.
+ - Normalize line endings on tests.
+v0.9.0:
+ date: 2014-01-07
+ changes:
+ - Bump to less 1.6
+v0.8.3:
+ date: 2013-12-06
+ changes:
+ - Support sourceMapURL
+v0.8.2:
+ date: 2013-11-14
+ changes:
+ - Support outputSourceFiles
+v0.8.1:
+ date: 2013-10-24
+ changes:
+ - Support sourceMapFilename, sourceMapBasepath and sourceMapRootpath
+v0.8.0:
+ date: 2013-10-22
+ changes:
+ - Upgrade to LESS 1.5
+ - Support strictUnits option
+ - Support sourceMap option
+ - Add customFunctions option for defining custom functions within LESS
+ - Output the source file name on error
+ - yuicompress option now cleancss (Less changed underlying dependency)
+v0.7.0:
+ date: 2013-08-08
+ changes:
+ - Downgrade no source files warning to only in verbose mode
+v0.6.5:
+ date: 2013-08-08
+ changes:
+ - Support strictMath option
+ - Support rootpath parse option
+v0.6.4:
+ date: 2013-07-09
+ changes:
+ - Support relativeUrls option
+v0.6.3:
+ date: 2013-07-06
+ changes:
+ - Add report option for minification and gzip results
+v0.6.2:
+ date: 2013-07-03
+ changes:
+ - support syncImport
+v0.6.1:
+ date: 2013-06-12
+ changes:
+ - Support ieCompat
+v0.6.0:
+ date: 2013-06-09
+ changes:
+ - Bump less to 1.4.0
+v0.5.2:
+ date: 2013-05-23
+ changes:
+ - Improve error handling.
+v0.5.1:
+ date: 2013-04-25
+ changes:
+ - Gracefully handle configuration without sources.
+v0.5.0:
+ date: 2013-02-15
+ changes:
+ - First official release for Grunt 0.4.0.
+v0.5.0rc7:
+ date: 2013-01-23
+ changes:
+ - Updating grunt/gruntplugin dependencies to rc7.
+ - Changing in-development grunt/gruntplugin dependency versions from tilde version ranges to specific versions.
+ - Remove experimental wildcard destination support.
+ - Switching to this.files api.
+v0.3.2:
+ date: 2012-10-18
+ changes:
+ - Add support for dumpLineNumbers.
+v0.3.1:
+ date: 2012-10-12
+ changes:
+ - Rename grunt-contrib-lib dep to grunt-lib-contrib.
+v0.3.0:
+ date: 2012-09-24
+ changes:
+ - Global options depreciated
+ - Revert normalize linefeeds.
+v0.2.2:
+ date: 2012-09-16
+ changes:
+ - Support all less options
+ - Normalize linefeeds
+ - Default path to dirname of src file.
+v0.2.0:
+ date: 2012-09-10
+ changes:
+ - Refactored from grunt-contrib into individual repo.
--- /dev/null
+Copyright (c) 2014 Tyler Kellen, 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.
\ No newline at end of file
--- /dev/null
+# grunt-contrib-less v1.0.1 [](https://travis-ci.org/gruntjs/grunt-contrib-less) [](https://ci.appveyor.com/project/gruntjs/grunt-contrib-less/branch/master)
+
+> Compile LESS files to CSS.
+
+
+
+## Getting Started
+This plugin requires Grunt `~0.4.5`
+
+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-less --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-less');
+```
+
+*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-less/tree/grunt-0.3-stable).*
+
+
+## Less task
+_Run this task with the `grunt less` command._
+
+Task targets, files and options may be specified according to the grunt [Configuring tasks](http://gruntjs.com/configuring-tasks) guide.
+### Options
+
+#### paths
+Type: `String` `Array` `Function`
+Default: Directory of input file.
+
+Specifies directories to scan for @import directives when parsing. Default value is the directory of the source, which is probably what you want.
+
+If you specify a function the source filepath will be the first argument. You can return either a string or an array of paths to be used.
+
+#### rootpath
+Type: `String`
+Default: `""`
+
+A path to add on to the start of every URL resource.
+
+#### compress
+Type: `Boolean`
+Default: `false`
+
+Compress output by removing some whitespaces.
+
+#### plugins
+Type: `Array`
+Default: `null`
+
+Allows passing plugins
+
+#### ieCompat
+Type: `Boolean`
+Default: `true`
+
+Enforce the CSS output is compatible with Internet Explorer 8.
+
+For example, the [data-uri](http://lesscss.org/functions/#misc-functions-data-uri) function encodes a file in base64 encoding and embeds it into the generated CSS files as a data-URI. Because Internet Explorer 8 limits `data-uri`s to 32KB, the ieCompat option prevents `less` from exceeding this.
+
+#### optimization
+Type: `Integer`
+Default: `null`
+
+Set the parser's optimization level. The lower the number, the less nodes it will create in the tree. This could matter for debugging, or if you want to access the individual nodes in the tree.
+
+#### strictImports
+Type: `Boolean`
+Default: `false`
+
+Force evaluation of imports.
+
+#### strictMath
+Type: `Boolean`
+Default: `false`
+
+When enabled, math is required to be in parenthesis.
+
+#### strictUnits
+Type: `Boolean`
+Default: `false`
+
+When enabled, less will validate the units used (e.g. 4px/2px = 2, not 2px and 4em/2px throws an error).
+
+#### syncImport
+Type: `Boolean`
+Default: `false`
+
+Read @import'ed files synchronously from disk.
+
+#### dumpLineNumbers
+Type: `String`
+Default: `false`
+
+Configures -sass-debug-info support.
+
+Accepts following values: `comments`, `mediaquery`, `all`.
+
+#### relativeUrls
+Type: `Boolean`
+Default: `false`
+
+Rewrite URLs to be relative. false: do not modify URLs.
+
+#### customFunctions
+Type: `Object`
+Default: none
+
+Define custom functions to be available within your LESS stylesheets. The function's name must be lowercase.
+In the definition, the first argument is the less object, and subsequent arguments are from the less function call.
+Values passed to the function are types defined within less, the return value may be either one of them or primitive.
+See the LESS documentation for more information on the available types.
+
+#### sourceMap
+Type: `Boolean`
+Default: `false`
+
+Enable source maps.
+
+#### sourceMapFilename
+Type: `String`
+Default: none
+
+Write the source map to a separate file with the given filename.
+
+#### sourceMapURL
+Type: `String`
+Default: none
+
+Override the default URL that points to the source map from the compiled CSS file.
+
+#### sourceMapBasepath
+Type: `String`
+Default: none
+
+Sets the base path for the less file paths in the source map.
+
+#### sourceMapRootpath
+Type: `String`
+Default: none
+
+Adds this path onto the less file paths in the source map.
+
+#### outputSourceFiles
+Type: `Boolean`
+Default: false
+
+Puts the less files into the map instead of referencing them.
+
+#### modifyVars
+Type: `Object`
+Default: none
+
+Overrides global variables. Equivalent to `--modify-vars='VAR=VALUE'` option in less.
+
+#### banner
+Type: `String`
+Default: none
+
+### Usage Examples
+
+```js
+less: {
+ development: {
+ options: {
+ paths: ["assets/css"]
+ },
+ files: {
+ "path/to/result.css": "path/to/source.less"
+ }
+ },
+ production: {
+ options: {
+ paths: ["assets/css"],
+ plugins: [
+ new require('less-plugin-autoprefix')({browsers: ["last 2 versions"]}),
+ new require('less-plugin-clean-css')(cleanCssOptions)
+ ],
+ modifyVars: {
+ imgPath: '"http://mycdn.com/path/to/images"',
+ bgColor: 'red'
+ }
+ },
+ files: {
+ "path/to/result.css": "path/to/source.less"
+ }
+ }
+}
+```
+
+
+## Release History
+
+ * 2015-04-07 v1.0.1 Fix backwards compat, lock to ~2.4
+ * 2014-12-23 v1.0.0 Update to less 2.1.0
+ * 2014-10-23 v0.12.0 Added cleancssOptions as a option. When possible, includes filename for failed writes. Ensure banner only outputs on the first file in a series.
+ * 2014-07-29 v0.11.4 Fix 'banner', 'urlArgs' LESS options Fixes npm 2 peerDependencies issues
+ * 2014-06-20 v0.11.3 Update to Less ~1.7.2.
+ * 2014-06-01 v0.11.2 Lock to less 1.7.0.
+ * 2014-05-26 v0.11.1 Fix `modifyVars` to work when less file ends with a comment.
+ * 2014-03-19 v0.11.0 Custom functions can return types defined by less paths option now accepts a function Replaced deprecated grunt.util methods Removes deprecated grunt.lib.contrib
+ * 2014-03-01 v0.10.0 sourceMapBasepath accepts a function. Update copyright to 2014. Update .gitattributes. Update less.js to v1.7.0. Prevent CRLF in the repo. Adds modify-vars option. Changed to async stack call. Fixes data-uri test. Normalize line endings on tests.
+ * 2014-01-07 v0.9.0 Bump to less 1.6
+ * 2013-12-06 v0.8.3 Support sourceMapURL
+ * 2013-11-14 v0.8.2 Support outputSourceFiles
+ * 2013-10-24 v0.8.1 Support sourceMapFilename, sourceMapBasepath and sourceMapRootpath
+ * 2013-10-22 v0.8.0 Upgrade to LESS 1.5 Support strictUnits option Support sourceMap option Add customFunctions option for defining custom functions within LESS Output the source file name on error yuicompress option now cleancss (Less changed underlying dependency)
+ * 2013-08-08 v0.7.0 Downgrade no source files warning to only in verbose mode
+ * 2013-08-08 v0.6.5 Support strictMath option Support rootpath parse option
+ * 2013-07-09 v0.6.4 Support relativeUrls option
+ * 2013-07-06 v0.6.3 Add report option for minification and gzip results
+ * 2013-07-03 v0.6.2 support syncImport
+ * 2013-06-12 v0.6.1 Support ieCompat
+ * 2013-06-09 v0.6.0 Bump less to 1.4.0
+ * 2013-05-23 v0.5.2 Improve error handling.
+ * 2013-04-25 v0.5.1 Gracefully handle configuration without sources.
+ * 2013-02-15 v0.5.0 First official release for Grunt 0.4.0.
+ * 2013-01-23 v0.5.0rc7 Updating grunt/gruntplugin dependencies to rc7. Changing in-development grunt/gruntplugin dependency versions from tilde version ranges to specific versions. Remove experimental wildcard destination support. Switching to this.files api.
+ * 2012-10-18 v0.3.2 Add support for dumpLineNumbers.
+ * 2012-10-12 v0.3.1 Rename grunt-contrib-lib dep to grunt-lib-contrib.
+ * 2012-09-24 v0.3.0 Global options depreciated Revert normalize linefeeds.
+ * 2012-09-16 v0.2.2 Support all less options Normalize linefeeds Default path to dirname of src file.
+ * 2012-09-10 v0.2.0 Refactored from grunt-contrib into individual repo.
+
+---
+
+Task submitted by [Tyler Kellen](http://goingslowly.com/)
+
+*This file was generated on Tue Apr 07 2015 15:04:23.*
--- /dev/null
+../less/bin/lessc
\ No newline at end of file
--- /dev/null
+language: node_js
+node_js:
+ - "0.10"
+ - "0.12"
+ - "iojs"
--- /dev/null
+Copyright (c) 2010-2014 Caolan McMahon
+
+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.
--- /dev/null
+# Async.js
+
+[](https://travis-ci.org/caolan/async)
+
+
+Async is a utility module which provides straight-forward, powerful functions
+for working with asynchronous JavaScript. Although originally designed for
+use with [Node.js](http://nodejs.org) and installable via `npm install async`,
+it can also be used directly in the browser.
+
+Async is also installable via:
+
+- [bower](http://bower.io/): `bower install async`
+- [component](https://github.com/component/component): `component install
+ caolan/async`
+- [jam](http://jamjs.org/): `jam install async`
+- [spm](http://spmjs.io/): `spm install async`
+
+Async provides around 20 functions that include the usual 'functional'
+suspects (`map`, `reduce`, `filter`, `each`…) as well as some common patterns
+for asynchronous control flow (`parallel`, `series`, `waterfall`…). All these
+functions assume you follow the Node.js convention of providing a single
+callback as the last argument of your `async` function.
+
+
+## Quick Examples
+
+```javascript
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+
+async.filter(['file1','file2','file3'], fs.exists, function(results){
+ // results now equals an array of the existing files
+});
+
+async.parallel([
+ function(){ ... },
+ function(){ ... }
+], callback);
+
+async.series([
+ function(){ ... },
+ function(){ ... }
+]);
+```
+
+There are many more functions available so take a look at the docs below for a
+full list. This module aims to be comprehensive, so if you feel anything is
+missing please create a GitHub issue for it.
+
+## Common Pitfalls
+
+### Binding a context to an iterator
+
+This section is really about `bind`, not about `async`. If you are wondering how to
+make `async` execute your iterators in a given context, or are confused as to why
+a method of another library isn't working as an iterator, study this example:
+
+```js
+// Here is a simple object with an (unnecessarily roundabout) squaring method
+var AsyncSquaringLibrary = {
+ squareExponent: 2,
+ square: function(number, callback){
+ var result = Math.pow(number, this.squareExponent);
+ setTimeout(function(){
+ callback(null, result);
+ }, 200);
+ }
+};
+
+async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){
+ // result is [NaN, NaN, NaN]
+ // This fails because the `this.squareExponent` expression in the square
+ // function is not evaluated in the context of AsyncSquaringLibrary, and is
+ // therefore undefined.
+});
+
+async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){
+ // result is [1, 4, 9]
+ // With the help of bind we can attach a context to the iterator before
+ // passing it to async. Now the square function will be executed in its
+ // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`
+ // will be as expected.
+});
+```
+
+## Download
+
+The source is available for download from
+[GitHub](http://github.com/caolan/async).
+Alternatively, you can install using Node Package Manager (`npm`):
+
+ npm install async
+
+__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed
+
+## In the Browser
+
+So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5.
+
+Usage:
+
+```html
+<script type="text/javascript" src="async.js"></script>
+<script type="text/javascript">
+
+ async.map(data, asyncProcess, function(err, results){
+ alert(results);
+ });
+
+</script>
+```
+
+## Documentation
+
+### Collections
+
+* [`each`](#each)
+* [`eachSeries`](#eachSeries)
+* [`eachLimit`](#eachLimit)
+* [`map`](#map)
+* [`mapSeries`](#mapSeries)
+* [`mapLimit`](#mapLimit)
+* [`filter`](#filter)
+* [`filterSeries`](#filterSeries)
+* [`reject`](#reject)
+* [`rejectSeries`](#rejectSeries)
+* [`reduce`](#reduce)
+* [`reduceRight`](#reduceRight)
+* [`detect`](#detect)
+* [`detectSeries`](#detectSeries)
+* [`sortBy`](#sortBy)
+* [`some`](#some)
+* [`every`](#every)
+* [`concat`](#concat)
+* [`concatSeries`](#concatSeries)
+
+### Control Flow
+
+* [`series`](#seriestasks-callback)
+* [`parallel`](#parallel)
+* [`parallelLimit`](#parallellimittasks-limit-callback)
+* [`whilst`](#whilst)
+* [`doWhilst`](#doWhilst)
+* [`until`](#until)
+* [`doUntil`](#doUntil)
+* [`forever`](#forever)
+* [`waterfall`](#waterfall)
+* [`compose`](#compose)
+* [`seq`](#seq)
+* [`applyEach`](#applyEach)
+* [`applyEachSeries`](#applyEachSeries)
+* [`queue`](#queue)
+* [`priorityQueue`](#priorityQueue)
+* [`cargo`](#cargo)
+* [`auto`](#auto)
+* [`retry`](#retry)
+* [`iterator`](#iterator)
+* [`apply`](#apply)
+* [`nextTick`](#nextTick)
+* [`times`](#times)
+* [`timesSeries`](#timesSeries)
+
+### Utils
+
+* [`memoize`](#memoize)
+* [`unmemoize`](#unmemoize)
+* [`log`](#log)
+* [`dir`](#dir)
+* [`noConflict`](#noConflict)
+
+
+## Collections
+
+<a name="forEach" />
+<a name="each" />
+### each(arr, iterator, callback)
+
+Applies the function `iterator` to each item in `arr`, in parallel.
+The `iterator` is called with an item from the list, and a callback for when it
+has finished. If the `iterator` passes an error to its `callback`, the main
+`callback` (for the `each` function) is immediately called with the error.
+
+Note, that since this function applies `iterator` to each item in parallel,
+there is no guarantee that the iterator functions will complete in order.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err)` which must be called once it has
+ completed. If no error has occurred, the `callback` should be run without
+ arguments or with an explicit `null` argument.
+* `callback(err)` - A callback which is called when all `iterator` functions
+ have finished, or an error occurs.
+
+__Examples__
+
+
+```js
+// assuming openFiles is an array of file names and saveFile is a function
+// to save the modified contents of that file:
+
+async.each(openFiles, saveFile, function(err){
+ // if any of the saves produced an error, err would equal that error
+});
+```
+
+```js
+// assuming openFiles is an array of file names
+
+async.each(openFiles, function(file, callback) {
+
+ // Perform operation on file here.
+ console.log('Processing file ' + file);
+
+ if( file.length > 32 ) {
+ console.log('This file name is too long');
+ callback('File name too long');
+ } else {
+ // Do work to process file here
+ console.log('File processed');
+ callback();
+ }
+}, function(err){
+ // if any of the file processing produced an error, err would equal that error
+ if( err ) {
+ // One of the iterations produced an error.
+ // All processing will now stop.
+ console.log('A file failed to process');
+ } else {
+ console.log('All files have been processed successfully');
+ }
+});
+```
+
+---------------------------------------
+
+<a name="forEachSeries" />
+<a name="eachSeries" />
+### eachSeries(arr, iterator, callback)
+
+The same as [`each`](#each), only `iterator` is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+This means the `iterator` functions will complete in order.
+
+
+---------------------------------------
+
+<a name="forEachLimit" />
+<a name="eachLimit" />
+### eachLimit(arr, limit, iterator, callback)
+
+The same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously
+running at any time.
+
+Note that the items in `arr` are not processed in batches, so there is no guarantee that
+the first `limit` `iterator` functions will complete before any others are started.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `limit` - The maximum number of `iterator`s to run at any time.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err)` which must be called once it has
+ completed. If no error has occurred, the callback should be run without
+ arguments or with an explicit `null` argument.
+* `callback(err)` - A callback which is called when all `iterator` functions
+ have finished, or an error occurs.
+
+__Example__
+
+```js
+// Assume documents is an array of JSON objects and requestApi is a
+// function that interacts with a rate-limited REST api.
+
+async.eachLimit(documents, 20, requestApi, function(err){
+ // if any of the saves produced an error, err would equal that error
+});
+```
+
+---------------------------------------
+
+<a name="map" />
+### map(arr, iterator, callback)
+
+Produces a new array of values by mapping each value in `arr` through
+the `iterator` function. The `iterator` is called with an item from `arr` and a
+callback for when it has finished processing. Each of these callback takes 2 arguments:
+an `error`, and the transformed item from `arr`. If `iterator` passes an error to his
+callback, the main `callback` (for the `map` function) is immediately called with the error.
+
+Note, that since this function applies the `iterator` to each item in parallel,
+there is no guarantee that the `iterator` functions will complete in order.
+However, the results array will be in the same order as the original `arr`.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, transformed)` which must be called once
+ it has completed with an error (which can be `null`) and a transformed item.
+* `callback(err, results)` - A callback which is called when all `iterator`
+ functions have finished, or an error occurs. Results is an array of the
+ transformed items from the `arr`.
+
+__Example__
+
+```js
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
+<a name="mapSeries" />
+### mapSeries(arr, iterator, callback)
+
+The same as [`map`](#map), only the `iterator` is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+The results array will be in the same order as the original.
+
+
+---------------------------------------
+
+<a name="mapLimit" />
+### mapLimit(arr, limit, iterator, callback)
+
+The same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously
+running at any time.
+
+Note that the items are not processed in batches, so there is no guarantee that
+the first `limit` `iterator` functions will complete before any others are started.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `limit` - The maximum number of `iterator`s to run at any time.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, transformed)` which must be called once
+ it has completed with an error (which can be `null`) and a transformed item.
+* `callback(err, results)` - A callback which is called when all `iterator`
+ calls have finished, or an error occurs. The result is an array of the
+ transformed items from the original `arr`.
+
+__Example__
+
+```js
+async.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
+<a name="select" />
+<a name="filter" />
+### filter(arr, iterator, callback)
+
+__Alias:__ `select`
+
+Returns a new array of all the values in `arr` which pass an async truth test.
+_The callback for each `iterator` call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`. This operation is
+performed in parallel, but the results array will be in the same order as the
+original.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
+ The `iterator` is passed a `callback(truthValue)`, which must be called with a
+ boolean argument once it has completed.
+* `callback(results)` - A callback which is called after all the `iterator`
+ functions have finished.
+
+__Example__
+
+```js
+async.filter(['file1','file2','file3'], fs.exists, function(results){
+ // results now equals an array of the existing files
+});
+```
+
+---------------------------------------
+
+<a name="selectSeries" />
+<a name="filterSeries" />
+### filterSeries(arr, iterator, callback)
+
+__Alias:__ `selectSeries`
+
+The same as [`filter`](#filter) only the `iterator` is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+The results array will be in the same order as the original.
+
+---------------------------------------
+
+<a name="reject" />
+### reject(arr, iterator, callback)
+
+The opposite of [`filter`](#filter). Removes values that pass an `async` truth test.
+
+---------------------------------------
+
+<a name="rejectSeries" />
+### rejectSeries(arr, iterator, callback)
+
+The same as [`reject`](#reject), only the `iterator` is applied to each item in `arr`
+in series.
+
+
+---------------------------------------
+
+<a name="reduce" />
+### reduce(arr, memo, iterator, callback)
+
+__Aliases:__ `inject`, `foldl`
+
+Reduces `arr` into a single value using an async `iterator` to return
+each successive step. `memo` is the initial state of the reduction.
+This function only operates in series.
+
+For performance reasons, it may make sense to split a call to this function into
+a parallel map, and then use the normal `Array.prototype.reduce` on the results.
+This function is for situations where each step in the reduction needs to be async;
+if you can get the data before reducing it, then it's probably a good idea to do so.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `memo` - The initial state of the reduction.
+* `iterator(memo, item, callback)` - A function applied to each item in the
+ array to produce the next step in the reduction. The `iterator` is passed a
+ `callback(err, reduction)` which accepts an optional error as its first
+ argument, and the state of the reduction as the second. If an error is
+ passed to the callback, the reduction is stopped and the main `callback` is
+ immediately called with the error.
+* `callback(err, result)` - A callback which is called after all the `iterator`
+ functions have finished. Result is the reduced value.
+
+__Example__
+
+```js
+async.reduce([1,2,3], 0, function(memo, item, callback){
+ // pointless async:
+ process.nextTick(function(){
+ callback(null, memo + item)
+ });
+}, function(err, result){
+ // result is now equal to the last value of memo, which is 6
+});
+```
+
+---------------------------------------
+
+<a name="reduceRight" />
+### reduceRight(arr, memo, iterator, callback)
+
+__Alias:__ `foldr`
+
+Same as [`reduce`](#reduce), only operates on `arr` in reverse order.
+
+
+---------------------------------------
+
+<a name="detect" />
+### detect(arr, iterator, callback)
+
+Returns the first value in `arr` that passes an async truth test. The
+`iterator` is applied in parallel, meaning the first iterator to return `true` will
+fire the detect `callback` with that result. That means the result might not be
+the first item in the original `arr` (in terms of order) that passes the test.
+
+If order within the original `arr` is important, then look at [`detectSeries`](#detectSeries).
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
+ The iterator is passed a `callback(truthValue)` which must be called with a
+ boolean argument once it has completed.
+* `callback(result)` - A callback which is called as soon as any iterator returns
+ `true`, or after all the `iterator` functions have finished. Result will be
+ the first item in the array that passes the truth test (iterator) or the
+ value `undefined` if none passed.
+
+__Example__
+
+```js
+async.detect(['file1','file2','file3'], fs.exists, function(result){
+ // result now equals the first file in the list that exists
+});
+```
+
+---------------------------------------
+
+<a name="detectSeries" />
+### detectSeries(arr, iterator, callback)
+
+The same as [`detect`](#detect), only the `iterator` is applied to each item in `arr`
+in series. This means the result is always the first in the original `arr` (in
+terms of array order) that passes the truth test.
+
+
+---------------------------------------
+
+<a name="sortBy" />
+### sortBy(arr, iterator, callback)
+
+Sorts a list by the results of running each `arr` value through an async `iterator`.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, sortValue)` which must be called once it
+ has completed with an error (which can be `null`) and a value to use as the sort
+ criteria.
+* `callback(err, results)` - A callback which is called after all the `iterator`
+ functions have finished, or an error occurs. Results is the items from
+ the original `arr` sorted by the values returned by the `iterator` calls.
+
+__Example__
+
+```js
+async.sortBy(['file1','file2','file3'], function(file, callback){
+ fs.stat(file, function(err, stats){
+ callback(err, stats.mtime);
+ });
+}, function(err, results){
+ // results is now the original array of files sorted by
+ // modified date
+});
+```
+
+__Sort Order__
+
+By modifying the callback parameter the sorting order can be influenced:
+
+```js
+//ascending order
+async.sortBy([1,9,3,5], function(x, callback){
+ callback(null, x);
+}, function(err,result){
+ //result callback
+} );
+
+//descending order
+async.sortBy([1,9,3,5], function(x, callback){
+ callback(null, x*-1); //<- x*-1 instead of x, turns the order around
+}, function(err,result){
+ //result callback
+} );
+```
+
+---------------------------------------
+
+<a name="some" />
+### some(arr, iterator, callback)
+
+__Alias:__ `any`
+
+Returns `true` if at least one element in the `arr` satisfies an async test.
+_The callback for each iterator call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`. Once any iterator
+call returns `true`, the main `callback` is immediately called.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in the array
+ in parallel. The iterator is passed a callback(truthValue) which must be
+ called with a boolean argument once it has completed.
+* `callback(result)` - A callback which is called as soon as any iterator returns
+ `true`, or after all the iterator functions have finished. Result will be
+ either `true` or `false` depending on the values of the async tests.
+
+__Example__
+
+```js
+async.some(['file1','file2','file3'], fs.exists, function(result){
+ // if result is true then at least one of the files exists
+});
+```
+
+---------------------------------------
+
+<a name="every" />
+### every(arr, iterator, callback)
+
+__Alias:__ `all`
+
+Returns `true` if every element in `arr` satisfies an async test.
+_The callback for each `iterator` call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in the array
+ in parallel. The iterator is passed a callback(truthValue) which must be
+ called with a boolean argument once it has completed.
+* `callback(result)` - A callback which is called after all the `iterator`
+ functions have finished. Result will be either `true` or `false` depending on
+ the values of the async tests.
+
+__Example__
+
+```js
+async.every(['file1','file2','file3'], fs.exists, function(result){
+ // if result is true then every file exists
+});
+```
+
+---------------------------------------
+
+<a name="concat" />
+### concat(arr, iterator, callback)
+
+Applies `iterator` to each item in `arr`, concatenating the results. Returns the
+concatenated list. The `iterator`s are called in parallel, and the results are
+concatenated as they return. There is no guarantee that the results array will
+be returned in the original order of `arr` passed to the `iterator` function.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, results)` which must be called once it
+ has completed with an error (which can be `null`) and an array of results.
+* `callback(err, results)` - A callback which is called after all the `iterator`
+ functions have finished, or an error occurs. Results is an array containing
+ the concatenated results of the `iterator` function.
+
+__Example__
+
+```js
+async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
+ // files is now a list of filenames that exist in the 3 directories
+});
+```
+
+---------------------------------------
+
+<a name="concatSeries" />
+### concatSeries(arr, iterator, callback)
+
+Same as [`concat`](#concat), but executes in series instead of parallel.
+
+
+## Control Flow
+
+<a name="series" />
+### series(tasks, [callback])
+
+Run the functions in the `tasks` array in series, each one running once the previous
+function has completed. If any functions in the series pass an error to its
+callback, no more functions are run, and `callback` is immediately called with the value of the error.
+Otherwise, `callback` receives an array of results when `tasks` have completed.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function, and the results will be passed to the final `callback` as an object
+instead of an array. This can be a more readable way of handling results from
+[`series`](#series).
+
+**Note** that while many implementations preserve the order of object properties, the
+[ECMAScript Language Specifcation](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
+explicitly states that
+
+> The mechanics and order of enumerating the properties is not specified.
+
+So if you rely on the order in which your series of functions are executed, and want
+this to work on all platforms, consider using an array.
+
+__Arguments__
+
+* `tasks` - An array or object containing functions to run, each function is passed
+ a `callback(err, result)` it must call on completion with an error `err` (which can
+ be `null`) and an optional `result` value.
+* `callback(err, results)` - An optional callback to run once all the functions
+ have completed. This function gets a results array (or object) containing all
+ the result arguments passed to the `task` callbacks.
+
+__Example__
+
+```js
+async.series([
+ function(callback){
+ // do some stuff ...
+ callback(null, 'one');
+ },
+ function(callback){
+ // do some more stuff ...
+ callback(null, 'two');
+ }
+],
+// optional callback
+function(err, results){
+ // results is now equal to ['one', 'two']
+});
+
+
+// an example using an object instead of an array
+async.series({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
+ },
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ }
+},
+function(err, results) {
+ // results is now equal to: {one: 1, two: 2}
+});
+```
+
+---------------------------------------
+
+<a name="parallel" />
+### parallel(tasks, [callback])
+
+Run the `tasks` array of functions in parallel, without waiting until the previous
+function has completed. If any of the functions pass an error to its
+callback, the main `callback` is immediately called with the value of the error.
+Once the `tasks` have completed, the results are passed to the final `callback` as an
+array.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function and the results will be passed to the final `callback` as an object
+instead of an array. This can be a more readable way of handling results from
+[`parallel`](#parallel).
+
+
+__Arguments__
+
+* `tasks` - An array or object containing functions to run. Each function is passed
+ a `callback(err, result)` which it must call on completion with an error `err`
+ (which can be `null`) and an optional `result` value.
+* `callback(err, results)` - An optional callback to run once all the functions
+ have completed. This function gets a results array (or object) containing all
+ the result arguments passed to the task callbacks.
+
+__Example__
+
+```js
+async.parallel([
+ function(callback){
+ setTimeout(function(){
+ callback(null, 'one');
+ }, 200);
+ },
+ function(callback){
+ setTimeout(function(){
+ callback(null, 'two');
+ }, 100);
+ }
+],
+// optional callback
+function(err, results){
+ // the results array will equal ['one','two'] even though
+ // the second function had a shorter timeout.
+});
+
+
+// an example using an object instead of an array
+async.parallel({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
+ },
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ }
+},
+function(err, results) {
+ // results is now equals to: {one: 1, two: 2}
+});
+```
+
+---------------------------------------
+
+<a name="parallelLimit" />
+### parallelLimit(tasks, limit, [callback])
+
+The same as [`parallel`](#parallel), only `tasks` are executed in parallel
+with a maximum of `limit` tasks executing at any time.
+
+Note that the `tasks` are not executed in batches, so there is no guarantee that
+the first `limit` tasks will complete before any others are started.
+
+__Arguments__
+
+* `tasks` - An array or object containing functions to run, each function is passed
+ a `callback(err, result)` it must call on completion with an error `err` (which can
+ be `null`) and an optional `result` value.
+* `limit` - The maximum number of `tasks` to run at any time.
+* `callback(err, results)` - An optional callback to run once all the functions
+ have completed. This function gets a results array (or object) containing all
+ the result arguments passed to the `task` callbacks.
+
+---------------------------------------
+
+<a name="whilst" />
+### whilst(test, fn, callback)
+
+Repeatedly call `fn`, while `test` returns `true`. Calls `callback` when stopped,
+or an error occurs.
+
+__Arguments__
+
+* `test()` - synchronous truth test to perform before each execution of `fn`.
+* `fn(callback)` - A function which is called each time `test` passes. The function is
+ passed a `callback(err)`, which must be called once it has completed with an
+ optional `err` argument.
+* `callback(err)` - A callback which is called after the test fails and repeated
+ execution of `fn` has stopped.
+
+__Example__
+
+```js
+var count = 0;
+
+async.whilst(
+ function () { return count < 5; },
+ function (callback) {
+ count++;
+ setTimeout(callback, 1000);
+ },
+ function (err) {
+ // 5 seconds have passed
+ }
+);
+```
+
+---------------------------------------
+
+<a name="doWhilst" />
+### doWhilst(fn, test, callback)
+
+The post-check version of [`whilst`](#whilst). To reflect the difference in
+the order of operations, the arguments `test` and `fn` are switched.
+
+`doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
+
+---------------------------------------
+
+<a name="until" />
+### until(test, fn, callback)
+
+Repeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,
+or an error occurs.
+
+The inverse of [`whilst`](#whilst).
+
+---------------------------------------
+
+<a name="doUntil" />
+### doUntil(fn, test, callback)
+
+Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.
+
+---------------------------------------
+
+<a name="forever" />
+### forever(fn, errback)
+
+Calls the asynchronous function `fn` with a callback parameter that allows it to
+call itself again, in series, indefinitely.
+
+If an error is passed to the callback then `errback` is called with the
+error, and execution stops, otherwise it will never be called.
+
+```js
+async.forever(
+ function(next) {
+ // next is suitable for passing to things that need a callback(err [, whatever]);
+ // it will result in this function being called again.
+ },
+ function(err) {
+ // if next is called with a value in its first parameter, it will appear
+ // in here as 'err', and execution will stop.
+ }
+);
+```
+
+---------------------------------------
+
+<a name="waterfall" />
+### waterfall(tasks, [callback])
+
+Runs the `tasks` array of functions in series, each passing their results to the next in
+the array. However, if any of the `tasks` pass an error to their own callback, the
+next function is not executed, and the main `callback` is immediately called with
+the error.
+
+__Arguments__
+
+* `tasks` - An array of functions to run, each function is passed a
+ `callback(err, result1, result2, ...)` it must call on completion. The first
+ argument is an error (which can be `null`) and any further arguments will be
+ passed as arguments in order to the next task.
+* `callback(err, [results])` - An optional callback to run once all the functions
+ have completed. This will be passed the results of the last task's callback.
+
+
+
+__Example__
+
+```js
+async.waterfall([
+ function(callback) {
+ callback(null, 'one', 'two');
+ },
+ function(arg1, arg2, callback) {
+ // arg1 now equals 'one' and arg2 now equals 'two'
+ callback(null, 'three');
+ },
+ function(arg1, callback) {
+ // arg1 now equals 'three'
+ callback(null, 'done');
+ }
+], function (err, result) {
+ // result now equals 'done'
+});
+```
+
+---------------------------------------
+<a name="compose" />
+### compose(fn1, fn2...)
+
+Creates a function which is a composition of the passed asynchronous
+functions. Each function consumes the return value of the function that
+follows. Composing functions `f()`, `g()`, and `h()` would produce the result of
+`f(g(h()))`, only this version uses callbacks to obtain the return values.
+
+Each function is executed with the `this` binding of the composed function.
+
+__Arguments__
+
+* `functions...` - the asynchronous functions to compose
+
+
+__Example__
+
+```js
+function add1(n, callback) {
+ setTimeout(function () {
+ callback(null, n + 1);
+ }, 10);
+}
+
+function mul3(n, callback) {
+ setTimeout(function () {
+ callback(null, n * 3);
+ }, 10);
+}
+
+var add1mul3 = async.compose(mul3, add1);
+
+add1mul3(4, function (err, result) {
+ // result now equals 15
+});
+```
+
+---------------------------------------
+<a name="seq" />
+### seq(fn1, fn2...)
+
+Version of the compose function that is more natural to read.
+Each function consumes the return value of the previous function.
+It is the equivalent of [`compose`](#compose) with the arguments reversed.
+
+Each function is executed with the `this` binding of the composed function.
+
+__Arguments__
+
+* functions... - the asynchronous functions to compose
+
+
+__Example__
+
+```js
+// Requires lodash (or underscore), express3 and dresende's orm2.
+// Part of an app, that fetches cats of the logged user.
+// This example uses `seq` function to avoid overnesting and error
+// handling clutter.
+app.get('/cats', function(request, response) {
+ var User = request.models.User;
+ async.seq(
+ _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data))
+ function(user, fn) {
+ user.getCats(fn); // 'getCats' has signature (callback(err, data))
+ }
+ )(req.session.user_id, function (err, cats) {
+ if (err) {
+ console.error(err);
+ response.json({ status: 'error', message: err.message });
+ } else {
+ response.json({ status: 'ok', message: 'Cats found', data: cats });
+ }
+ });
+});
+```
+
+---------------------------------------
+<a name="applyEach" />
+### applyEach(fns, args..., callback)
+
+Applies the provided arguments to each function in the array, calling
+`callback` after all functions have completed. If you only provide the first
+argument, then it will return a function which lets you pass in the
+arguments as if it were a single function call.
+
+__Arguments__
+
+* `fns` - the asynchronous functions to all call with the same arguments
+* `args...` - any number of separate arguments to pass to the function
+* `callback` - the final argument should be the callback, called when all
+ functions have completed processing
+
+
+__Example__
+
+```js
+async.applyEach([enableSearch, updateSchema], 'bucket', callback);
+
+// partial application example:
+async.each(
+ buckets,
+ async.applyEach([enableSearch, updateSchema]),
+ callback
+);
+```
+
+---------------------------------------
+
+<a name="applyEachSeries" />
+### applyEachSeries(arr, iterator, callback)
+
+The same as [`applyEach`](#applyEach) only the functions are applied in series.
+
+---------------------------------------
+
+<a name="queue" />
+### queue(worker, concurrency)
+
+Creates a `queue` object with the specified `concurrency`. Tasks added to the
+`queue` are processed in parallel (up to the `concurrency` limit). If all
+`worker`s are in progress, the task is queued until one becomes available.
+Once a `worker` completes a `task`, that `task`'s callback is called.
+
+__Arguments__
+
+* `worker(task, callback)` - An asynchronous function for processing a queued
+ task, which must call its `callback(err)` argument when finished, with an
+ optional `error` as an argument.
+* `concurrency` - An `integer` for determining how many `worker` functions should be
+ run in parallel.
+
+__Queue objects__
+
+The `queue` object returned by this function has the following properties and
+methods:
+
+* `length()` - a function returning the number of items waiting to be processed.
+* `started` - a function returning whether or not any items have been pushed and processed by the queue
+* `running()` - a function returning the number of items currently being processed.
+* `idle()` - a function returning false if there are items waiting or being processed, or true if not.
+* `concurrency` - an integer for determining how many `worker` functions should be
+ run in parallel. This property can be changed after a `queue` is created to
+ alter the concurrency on-the-fly.
+* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once
+ the `worker` has finished processing the task. Instead of a single task, a `tasks` array
+ can be submitted. The respective callback is used for every task in the list.
+* `unshift(task, [callback])` - add a new task to the front of the `queue`.
+* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit,
+ and further tasks will be queued.
+* `empty` - a callback that is called when the last item from the `queue` is given to a `worker`.
+* `drain` - a callback that is called when the last item from the `queue` has returned from the `worker`.
+* `paused` - a boolean for determining whether the queue is in a paused state
+* `pause()` - a function that pauses the processing of tasks until `resume()` is called.
+* `resume()` - a function that resumes the processing of queued tasks when the queue is paused.
+* `kill()` - a function that removes the `drain` callback and empties remaining tasks from the queue forcing it to go idle.
+
+__Example__
+
+```js
+// create a queue object with concurrency 2
+
+var q = async.queue(function (task, callback) {
+ console.log('hello ' + task.name);
+ callback();
+}, 2);
+
+
+// assign a callback
+q.drain = function() {
+ console.log('all items have been processed');
+}
+
+// add some items to the queue
+
+q.push({name: 'foo'}, function (err) {
+ console.log('finished processing foo');
+});
+q.push({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+
+// add some items to the queue (batch-wise)
+
+q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
+ console.log('finished processing item');
+});
+
+// add some items to the front of the queue
+
+q.unshift({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+```
+
+
+---------------------------------------
+
+<a name="priorityQueue" />
+### priorityQueue(worker, concurrency)
+
+The same as [`queue`](#queue) only tasks are assigned a priority and completed in ascending priority order. There are two differences between `queue` and `priorityQueue` objects:
+
+* `push(task, priority, [callback])` - `priority` should be a number. If an array of
+ `tasks` is given, all tasks will be assigned the same priority.
+* The `unshift` method was removed.
+
+---------------------------------------
+
+<a name="cargo" />
+### cargo(worker, [payload])
+
+Creates a `cargo` object with the specified payload. Tasks added to the
+cargo will be processed altogether (up to the `payload` limit). If the
+`worker` is in progress, the task is queued until it becomes available. Once
+the `worker` has completed some tasks, each callback of those tasks is called.
+Check out [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) for how `cargo` and `queue` work.
+
+While [queue](#queue) passes only one task to one of a group of workers
+at a time, cargo passes an array of tasks to a single worker, repeating
+when the worker is finished.
+
+__Arguments__
+
+* `worker(tasks, callback)` - An asynchronous function for processing an array of
+ queued tasks, which must call its `callback(err)` argument when finished, with
+ an optional `err` argument.
+* `payload` - An optional `integer` for determining how many tasks should be
+ processed per round; if omitted, the default is unlimited.
+
+__Cargo objects__
+
+The `cargo` object returned by this function has the following properties and
+methods:
+
+* `length()` - A function returning the number of items waiting to be processed.
+* `payload` - An `integer` for determining how many tasks should be
+ process per round. This property can be changed after a `cargo` is created to
+ alter the payload on-the-fly.
+* `push(task, [callback])` - Adds `task` to the `queue`. The callback is called
+ once the `worker` has finished processing the task. Instead of a single task, an array of `tasks`
+ can be submitted. The respective callback is used for every task in the list.
+* `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued.
+* `empty` - A callback that is called when the last item from the `queue` is given to a `worker`.
+* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`.
+
+__Example__
+
+```js
+// create a cargo object with payload 2
+
+var cargo = async.cargo(function (tasks, callback) {
+ for(var i=0; i<tasks.length; i++){
+ console.log('hello ' + tasks[i].name);
+ }
+ callback();
+}, 2);
+
+
+// add some items
+
+cargo.push({name: 'foo'}, function (err) {
+ console.log('finished processing foo');
+});
+cargo.push({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+cargo.push({name: 'baz'}, function (err) {
+ console.log('finished processing baz');
+});
+```
+
+---------------------------------------
+
+<a name="auto" />
+### auto(tasks, [callback])
+
+Determines the best order for running the functions in `tasks`, based on their
+requirements. Each function can optionally depend on other functions being completed
+first, and each function is run as soon as its requirements are satisfied.
+
+If any of the functions pass an error to their callback, it will not
+complete (so any other functions depending on it will not run), and the main
+`callback` is immediately called with the error. Functions also receive an
+object containing the results of functions which have completed so far.
+
+Note, all functions are called with a `results` object as a second argument,
+so it is unsafe to pass functions in the `tasks` object which cannot handle the
+extra argument.
+
+For example, this snippet of code:
+
+```js
+async.auto({
+ readData: async.apply(fs.readFile, 'data.txt', 'utf-8')
+}, callback);
+```
+
+will have the effect of calling `readFile` with the results object as the last
+argument, which will fail:
+
+```js
+fs.readFile('data.txt', 'utf-8', cb, {});
+```
+
+Instead, wrap the call to `readFile` in a function which does not forward the
+`results` object:
+
+```js
+async.auto({
+ readData: function(cb, results){
+ fs.readFile('data.txt', 'utf-8', cb);
+ }
+}, callback);
+```
+
+__Arguments__
+
+* `tasks` - An object. Each of its properties is either a function or an array of
+ requirements, with the function itself the last item in the array. The object's key
+ of a property serves as the name of the task defined by that property,
+ i.e. can be used when specifying requirements for other tasks.
+ The function receives two arguments: (1) a `callback(err, result)` which must be
+ called when finished, passing an `error` (which can be `null`) and the result of
+ the function's execution, and (2) a `results` object, containing the results of
+ the previously executed functions.
+* `callback(err, results)` - An optional callback which is called when all the
+ tasks have been completed. It receives the `err` argument if any `tasks`
+ pass an error to their callback. Results are always returned; however, if
+ an error occurs, no further `tasks` will be performed, and the results
+ object will only contain partial results.
+
+
+__Example__
+
+```js
+async.auto({
+ get_data: function(callback){
+ console.log('in get_data');
+ // async code to get some data
+ callback(null, 'data', 'converted to array');
+ },
+ make_folder: function(callback){
+ console.log('in make_folder');
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ callback(null, 'folder');
+ },
+ write_file: ['get_data', 'make_folder', function(callback, results){
+ console.log('in write_file', JSON.stringify(results));
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
+ callback(null, 'filename');
+ }],
+ email_link: ['write_file', function(callback, results){
+ console.log('in email_link', JSON.stringify(results));
+ // once the file is written let's email a link to it...
+ // results.write_file contains the filename returned by write_file.
+ callback(null, {'file':results.write_file, 'email':'user@example.com'});
+ }]
+}, function(err, results) {
+ console.log('err = ', err);
+ console.log('results = ', results);
+});
+```
+
+This is a fairly trivial example, but to do this using the basic parallel and
+series functions would look like this:
+
+```js
+async.parallel([
+ function(callback){
+ console.log('in get_data');
+ // async code to get some data
+ callback(null, 'data', 'converted to array');
+ },
+ function(callback){
+ console.log('in make_folder');
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ callback(null, 'folder');
+ }
+],
+function(err, results){
+ async.series([
+ function(callback){
+ console.log('in write_file', JSON.stringify(results));
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
+ results.push('filename');
+ callback(null);
+ },
+ function(callback){
+ console.log('in email_link', JSON.stringify(results));
+ // once the file is written let's email a link to it...
+ callback(null, {'file':results.pop(), 'email':'user@example.com'});
+ }
+ ]);
+});
+```
+
+For a complicated series of `async` tasks, using the [`auto`](#auto) function makes adding
+new tasks much easier (and the code more readable).
+
+
+---------------------------------------
+
+<a name="retry" />
+### retry([times = 5], task, [callback])
+
+Attempts to get a successful response from `task` no more than `times` times before
+returning an error. If the task is successful, the `callback` will be passed the result
+of the successful task. If all attempts fail, the callback will be passed the error and
+result (if any) of the final attempt.
+
+__Arguments__
+
+* `times` - An integer indicating how many times to attempt the `task` before giving up. Defaults to 5.
+* `task(callback, results)` - A function which receives two arguments: (1) a `callback(err, result)`
+ which must be called when finished, passing `err` (which can be `null`) and the `result` of
+ the function's execution, and (2) a `results` object, containing the results of
+ the previously executed functions (if nested inside another control flow).
+* `callback(err, results)` - An optional callback which is called when the
+ task has succeeded, or after the final failed attempt. It receives the `err` and `result` arguments of the last attempt at completing the `task`.
+
+The [`retry`](#retry) function can be used as a stand-alone control flow by passing a
+callback, as shown below:
+
+```js
+async.retry(3, apiMethod, function(err, result) {
+ // do something with the result
+});
+```
+
+It can also be embeded within other control flow functions to retry individual methods
+that are not as reliable, like this:
+
+```js
+async.auto({
+ users: api.getUsers.bind(api),
+ payments: async.retry(3, api.getPayments.bind(api))
+}, function(err, results) {
+ // do something with the results
+});
+```
+
+
+---------------------------------------
+
+<a name="iterator" />
+### iterator(tasks)
+
+Creates an iterator function which calls the next function in the `tasks` array,
+returning a continuation to call the next one after that. It's also possible to
+“peek” at the next iterator with `iterator.next()`.
+
+This function is used internally by the `async` module, but can be useful when
+you want to manually control the flow of functions in series.
+
+__Arguments__
+
+* `tasks` - An array of functions to run.
+
+__Example__
+
+```js
+var iterator = async.iterator([
+ function(){ sys.p('one'); },
+ function(){ sys.p('two'); },
+ function(){ sys.p('three'); }
+]);
+
+node> var iterator2 = iterator();
+'one'
+node> var iterator3 = iterator2();
+'two'
+node> iterator3();
+'three'
+node> var nextfn = iterator2.next();
+node> nextfn();
+'three'
+```
+
+---------------------------------------
+
+<a name="apply" />
+### apply(function, arguments..)
+
+Creates a continuation function with some arguments already applied.
+
+Useful as a shorthand when combined with other control flow functions. Any arguments
+passed to the returned function are added to the arguments originally passed
+to apply.
+
+__Arguments__
+
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to automatically apply when the
+ continuation is called.
+
+__Example__
+
+```js
+// using apply
+
+async.parallel([
+ async.apply(fs.writeFile, 'testfile1', 'test1'),
+ async.apply(fs.writeFile, 'testfile2', 'test2'),
+]);
+
+
+// the same process without using apply
+
+async.parallel([
+ function(callback){
+ fs.writeFile('testfile1', 'test1', callback);
+ },
+ function(callback){
+ fs.writeFile('testfile2', 'test2', callback);
+ }
+]);
+```
+
+It's possible to pass any number of additional arguments when calling the
+continuation:
+
+```js
+node> var fn = async.apply(sys.puts, 'one');
+node> fn('two', 'three');
+one
+two
+three
+```
+
+---------------------------------------
+
+<a name="nextTick" />
+### nextTick(callback), setImmediate(callback)
+
+Calls `callback` on a later loop around the event loop. In Node.js this just
+calls `process.nextTick`; in the browser it falls back to `setImmediate(callback)`
+if available, otherwise `setTimeout(callback, 0)`, which means other higher priority
+events may precede the execution of `callback`.
+
+This is used internally for browser-compatibility purposes.
+
+__Arguments__
+
+* `callback` - The function to call on a later loop around the event loop.
+
+__Example__
+
+```js
+var call_order = [];
+async.nextTick(function(){
+ call_order.push('two');
+ // call_order now equals ['one','two']
+});
+call_order.push('one')
+```
+
+<a name="times" />
+### times(n, callback)
+
+Calls the `callback` function `n` times, and accumulates results in the same manner
+you would use with [`map`](#map).
+
+__Arguments__
+
+* `n` - The number of times to run the function.
+* `callback` - The function to call `n` times.
+
+__Example__
+
+```js
+// Pretend this is some complicated async factory
+var createUser = function(id, callback) {
+ callback(null, {
+ id: 'user' + id
+ })
+}
+// generate 5 users
+async.times(5, function(n, next){
+ createUser(n, function(err, user) {
+ next(err, user)
+ })
+}, function(err, users) {
+ // we should now have 5 users
+});
+```
+
+<a name="timesSeries" />
+### timesSeries(n, callback)
+
+The same as [`times`](#times), only the iterator is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+The results array will be in the same order as the original.
+
+
+## Utils
+
+<a name="memoize" />
+### memoize(fn, [hasher])
+
+Caches the results of an `async` function. When creating a hash to store function
+results against, the callback is omitted from the hash and an optional hash
+function can be used.
+
+The cache of results is exposed as the `memo` property of the function returned
+by `memoize`.
+
+__Arguments__
+
+* `fn` - The function to proxy and cache results from.
+* `hasher` - Tn optional function for generating a custom hash for storing
+ results. It has all the arguments applied to it apart from the callback, and
+ must be synchronous.
+
+__Example__
+
+```js
+var slow_fn = function (name, callback) {
+ // do something
+ callback(null, result);
+};
+var fn = async.memoize(slow_fn);
+
+// fn can now be used as if it were slow_fn
+fn('some name', function () {
+ // callback
+});
+```
+
+<a name="unmemoize" />
+### unmemoize(fn)
+
+Undoes a [`memoize`](#memoize)d function, reverting it to the original, unmemoized
+form. Handy for testing.
+
+__Arguments__
+
+* `fn` - the memoized function
+
+<a name="log" />
+### log(function, arguments)
+
+Logs the result of an `async` function to the `console`. Only works in Node.js or
+in browsers that support `console.log` and `console.error` (such as FF and Chrome).
+If multiple arguments are returned from the async function, `console.log` is
+called on each argument in order.
+
+__Arguments__
+
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to apply to the function.
+
+__Example__
+
+```js
+var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, 'hello ' + name);
+ }, 1000);
+};
+```
+```js
+node> async.log(hello, 'world');
+'hello world'
+```
+
+---------------------------------------
+
+<a name="dir" />
+### dir(function, arguments)
+
+Logs the result of an `async` function to the `console` using `console.dir` to
+display the properties of the resulting object. Only works in Node.js or
+in browsers that support `console.dir` and `console.error` (such as FF and Chrome).
+If multiple arguments are returned from the async function, `console.dir` is
+called on each argument in order.
+
+__Arguments__
+
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to apply to the function.
+
+__Example__
+
+```js
+var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, {hello: name});
+ }, 1000);
+};
+```
+```js
+node> async.dir(hello, 'world');
+{hello: 'world'}
+```
+
+---------------------------------------
+
+<a name="noConflict" />
+### noConflict()
+
+Changes the value of `async` back to its original value, returning a reference to the
+`async` object.
--- /dev/null
+{
+ "name": "async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "version": "0.9.2",
+ "main": "lib/async.js",
+ "keywords": [
+ "async",
+ "callback",
+ "utility",
+ "module"
+ ],
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/caolan/async.git"
+ },
+ "devDependencies": {
+ "nodeunit": ">0.0.0",
+ "uglify-js": "1.2.x",
+ "nodelint": ">0.0.0",
+ "lodash": ">=2.4.1"
+ },
+ "moduleType": [
+ "amd",
+ "globals",
+ "node"
+ ],
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "authors": [
+ "Caolan McMahon"
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "version": "0.9.2",
+ "keywords": [
+ "async",
+ "callback",
+ "utility",
+ "module"
+ ],
+ "license": "MIT",
+ "repository": "caolan/async",
+ "scripts": [
+ "lib/async.js"
+ ]
+}
\ No newline at end of file
--- /dev/null
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+/*jshint onevar: false, indent:4 */
+/*global setImmediate: false, setTimeout: false, console: false */
+(function () {
+
+ var async = {};
+
+ // global on the server, window in the browser
+ var root, previous_async;
+
+ root = this;
+ if (root != null) {
+ previous_async = root.async;
+ }
+
+ async.noConflict = function () {
+ root.async = previous_async;
+ return async;
+ };
+
+ function only_once(fn) {
+ var called = false;
+ return function() {
+ if (called) throw new Error("Callback was already called.");
+ called = true;
+ fn.apply(root, arguments);
+ }
+ }
+
+ //// cross-browser compatiblity functions ////
+
+ var _toString = Object.prototype.toString;
+
+ var _isArray = Array.isArray || function (obj) {
+ return _toString.call(obj) === '[object Array]';
+ };
+
+ var _each = function (arr, iterator) {
+ for (var i = 0; i < arr.length; i += 1) {
+ iterator(arr[i], i, arr);
+ }
+ };
+
+ var _map = function (arr, iterator) {
+ if (arr.map) {
+ return arr.map(iterator);
+ }
+ var results = [];
+ _each(arr, function (x, i, a) {
+ results.push(iterator(x, i, a));
+ });
+ return results;
+ };
+
+ var _reduce = function (arr, iterator, memo) {
+ if (arr.reduce) {
+ return arr.reduce(iterator, memo);
+ }
+ _each(arr, function (x, i, a) {
+ memo = iterator(memo, x, i, a);
+ });
+ return memo;
+ };
+
+ var _keys = function (obj) {
+ if (Object.keys) {
+ return Object.keys(obj);
+ }
+ var keys = [];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+ return keys;
+ };
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+ if (typeof process === 'undefined' || !(process.nextTick)) {
+ if (typeof setImmediate === 'function') {
+ async.nextTick = function (fn) {
+ // not a direct alias for IE10 compatibility
+ setImmediate(fn);
+ };
+ async.setImmediate = async.nextTick;
+ }
+ else {
+ async.nextTick = function (fn) {
+ setTimeout(fn, 0);
+ };
+ async.setImmediate = async.nextTick;
+ }
+ }
+ else {
+ async.nextTick = process.nextTick;
+ if (typeof setImmediate !== 'undefined') {
+ async.setImmediate = function (fn) {
+ // not a direct alias for IE10 compatibility
+ setImmediate(fn);
+ };
+ }
+ else {
+ async.setImmediate = async.nextTick;
+ }
+ }
+
+ async.each = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ _each(arr, function (x) {
+ iterator(x, only_once(done) );
+ });
+ function done(err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed >= arr.length) {
+ callback();
+ }
+ }
+ }
+ };
+ async.forEach = async.each;
+
+ async.eachSeries = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ var iterate = function () {
+ iterator(arr[completed], function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed >= arr.length) {
+ callback();
+ }
+ else {
+ iterate();
+ }
+ }
+ });
+ };
+ iterate();
+ };
+ async.forEachSeries = async.eachSeries;
+
+ async.eachLimit = function (arr, limit, iterator, callback) {
+ var fn = _eachLimit(limit);
+ fn.apply(null, [arr, iterator, callback]);
+ };
+ async.forEachLimit = async.eachLimit;
+
+ var _eachLimit = function (limit) {
+
+ return function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length || limit <= 0) {
+ return callback();
+ }
+ var completed = 0;
+ var started = 0;
+ var running = 0;
+
+ (function replenish () {
+ if (completed >= arr.length) {
+ return callback();
+ }
+
+ while (running < limit && started < arr.length) {
+ started += 1;
+ running += 1;
+ iterator(arr[started - 1], function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ running -= 1;
+ if (completed >= arr.length) {
+ callback();
+ }
+ else {
+ replenish();
+ }
+ }
+ });
+ }
+ })();
+ };
+ };
+
+
+ var doParallel = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [async.each].concat(args));
+ };
+ };
+ var doParallelLimit = function(limit, fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [_eachLimit(limit)].concat(args));
+ };
+ };
+ var doSeries = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [async.eachSeries].concat(args));
+ };
+ };
+
+
+ var _asyncMap = function (eachfn, arr, iterator, callback) {
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ if (!callback) {
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (err) {
+ callback(err);
+ });
+ });
+ } else {
+ var results = [];
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (err, v) {
+ results[x.index] = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+ };
+ async.map = doParallel(_asyncMap);
+ async.mapSeries = doSeries(_asyncMap);
+ async.mapLimit = function (arr, limit, iterator, callback) {
+ return _mapLimit(limit)(arr, iterator, callback);
+ };
+
+ var _mapLimit = function(limit) {
+ return doParallelLimit(limit, _asyncMap);
+ };
+
+ // reduce only has a series version, as doing reduce in parallel won't
+ // work in many situations.
+ async.reduce = function (arr, memo, iterator, callback) {
+ async.eachSeries(arr, function (x, callback) {
+ iterator(memo, x, function (err, v) {
+ memo = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, memo);
+ });
+ };
+ // inject alias
+ async.inject = async.reduce;
+ // foldl alias
+ async.foldl = async.reduce;
+
+ async.reduceRight = function (arr, memo, iterator, callback) {
+ var reversed = _map(arr, function (x) {
+ return x;
+ }).reverse();
+ async.reduce(reversed, memo, iterator, callback);
+ };
+ // foldr alias
+ async.foldr = async.reduceRight;
+
+ var _filter = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (v) {
+ if (v) {
+ results.push(x);
+ }
+ callback();
+ });
+ }, function (err) {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ };
+ async.filter = doParallel(_filter);
+ async.filterSeries = doSeries(_filter);
+ // select alias
+ async.select = async.filter;
+ async.selectSeries = async.filterSeries;
+
+ var _reject = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (v) {
+ if (!v) {
+ results.push(x);
+ }
+ callback();
+ });
+ }, function (err) {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ };
+ async.reject = doParallel(_reject);
+ async.rejectSeries = doSeries(_reject);
+
+ var _detect = function (eachfn, arr, iterator, main_callback) {
+ eachfn(arr, function (x, callback) {
+ iterator(x, function (result) {
+ if (result) {
+ main_callback(x);
+ main_callback = function () {};
+ }
+ else {
+ callback();
+ }
+ });
+ }, function (err) {
+ main_callback();
+ });
+ };
+ async.detect = doParallel(_detect);
+ async.detectSeries = doSeries(_detect);
+
+ async.some = function (arr, iterator, main_callback) {
+ async.each(arr, function (x, callback) {
+ iterator(x, function (v) {
+ if (v) {
+ main_callback(true);
+ main_callback = function () {};
+ }
+ callback();
+ });
+ }, function (err) {
+ main_callback(false);
+ });
+ };
+ // any alias
+ async.any = async.some;
+
+ async.every = function (arr, iterator, main_callback) {
+ async.each(arr, function (x, callback) {
+ iterator(x, function (v) {
+ if (!v) {
+ main_callback(false);
+ main_callback = function () {};
+ }
+ callback();
+ });
+ }, function (err) {
+ main_callback(true);
+ });
+ };
+ // all alias
+ async.all = async.every;
+
+ async.sortBy = function (arr, iterator, callback) {
+ async.map(arr, function (x, callback) {
+ iterator(x, function (err, criteria) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, {value: x, criteria: criteria});
+ }
+ });
+ }, function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+ else {
+ var fn = function (left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ };
+ callback(null, _map(results.sort(fn), function (x) {
+ return x.value;
+ }));
+ }
+ });
+ };
+
+ async.auto = function (tasks, callback) {
+ callback = callback || function () {};
+ var keys = _keys(tasks);
+ var remainingTasks = keys.length
+ if (!remainingTasks) {
+ return callback();
+ }
+
+ var results = {};
+
+ var listeners = [];
+ var addListener = function (fn) {
+ listeners.unshift(fn);
+ };
+ var removeListener = function (fn) {
+ for (var i = 0; i < listeners.length; i += 1) {
+ if (listeners[i] === fn) {
+ listeners.splice(i, 1);
+ return;
+ }
+ }
+ };
+ var taskComplete = function () {
+ remainingTasks--
+ _each(listeners.slice(0), function (fn) {
+ fn();
+ });
+ };
+
+ addListener(function () {
+ if (!remainingTasks) {
+ var theCallback = callback;
+ // prevent final callback from calling itself if it errors
+ callback = function () {};
+
+ theCallback(null, results);
+ }
+ });
+
+ _each(keys, function (k) {
+ var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+ var taskCallback = function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ if (err) {
+ var safeResults = {};
+ _each(_keys(results), function(rkey) {
+ safeResults[rkey] = results[rkey];
+ });
+ safeResults[k] = args;
+ callback(err, safeResults);
+ // stop subsequent errors hitting callback multiple times
+ callback = function () {};
+ }
+ else {
+ results[k] = args;
+ async.setImmediate(taskComplete);
+ }
+ };
+ var requires = task.slice(0, Math.abs(task.length - 1)) || [];
+ var ready = function () {
+ return _reduce(requires, function (a, x) {
+ return (a && results.hasOwnProperty(x));
+ }, true) && !results.hasOwnProperty(k);
+ };
+ if (ready()) {
+ task[task.length - 1](taskCallback, results);
+ }
+ else {
+ var listener = function () {
+ if (ready()) {
+ removeListener(listener);
+ task[task.length - 1](taskCallback, results);
+ }
+ };
+ addListener(listener);
+ }
+ });
+ };
+
+ async.retry = function(times, task, callback) {
+ var DEFAULT_TIMES = 5;
+ var attempts = [];
+ // Use defaults if times not passed
+ if (typeof times === 'function') {
+ callback = task;
+ task = times;
+ times = DEFAULT_TIMES;
+ }
+ // Make sure times is a number
+ times = parseInt(times, 10) || DEFAULT_TIMES;
+ var wrappedTask = function(wrappedCallback, wrappedResults) {
+ var retryAttempt = function(task, finalAttempt) {
+ return function(seriesCallback) {
+ task(function(err, result){
+ seriesCallback(!err || finalAttempt, {err: err, result: result});
+ }, wrappedResults);
+ };
+ };
+ while (times) {
+ attempts.push(retryAttempt(task, !(times-=1)));
+ }
+ async.series(attempts, function(done, data){
+ data = data[data.length - 1];
+ (wrappedCallback || callback)(data.err, data.result);
+ });
+ }
+ // If a callback is passed, run this as a controll flow
+ return callback ? wrappedTask() : wrappedTask
+ };
+
+ async.waterfall = function (tasks, callback) {
+ callback = callback || function () {};
+ if (!_isArray(tasks)) {
+ var err = new Error('First argument to waterfall must be an array of functions');
+ return callback(err);
+ }
+ if (!tasks.length) {
+ return callback();
+ }
+ var wrapIterator = function (iterator) {
+ return function (err) {
+ if (err) {
+ callback.apply(null, arguments);
+ callback = function () {};
+ }
+ else {
+ var args = Array.prototype.slice.call(arguments, 1);
+ var next = iterator.next();
+ if (next) {
+ args.push(wrapIterator(next));
+ }
+ else {
+ args.push(callback);
+ }
+ async.setImmediate(function () {
+ iterator.apply(null, args);
+ });
+ }
+ };
+ };
+ wrapIterator(async.iterator(tasks))();
+ };
+
+ var _parallel = function(eachfn, tasks, callback) {
+ callback = callback || function () {};
+ if (_isArray(tasks)) {
+ eachfn.map(tasks, function (fn, callback) {
+ if (fn) {
+ fn(function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ callback.call(null, err, args);
+ });
+ }
+ }, callback);
+ }
+ else {
+ var results = {};
+ eachfn.each(_keys(tasks), function (k, callback) {
+ tasks[k](function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[k] = args;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+ };
+
+ async.parallel = function (tasks, callback) {
+ _parallel({ map: async.map, each: async.each }, tasks, callback);
+ };
+
+ async.parallelLimit = function(tasks, limit, callback) {
+ _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
+ };
+
+ async.series = function (tasks, callback) {
+ callback = callback || function () {};
+ if (_isArray(tasks)) {
+ async.mapSeries(tasks, function (fn, callback) {
+ if (fn) {
+ fn(function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ callback.call(null, err, args);
+ });
+ }
+ }, callback);
+ }
+ else {
+ var results = {};
+ async.eachSeries(_keys(tasks), function (k, callback) {
+ tasks[k](function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[k] = args;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+ };
+
+ async.iterator = function (tasks) {
+ var makeCallback = function (index) {
+ var fn = function () {
+ if (tasks.length) {
+ tasks[index].apply(null, arguments);
+ }
+ return fn.next();
+ };
+ fn.next = function () {
+ return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+ };
+ return fn;
+ };
+ return makeCallback(0);
+ };
+
+ async.apply = function (fn) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function () {
+ return fn.apply(
+ null, args.concat(Array.prototype.slice.call(arguments))
+ );
+ };
+ };
+
+ var _concat = function (eachfn, arr, fn, callback) {
+ var r = [];
+ eachfn(arr, function (x, cb) {
+ fn(x, function (err, y) {
+ r = r.concat(y || []);
+ cb(err);
+ });
+ }, function (err) {
+ callback(err, r);
+ });
+ };
+ async.concat = doParallel(_concat);
+ async.concatSeries = doSeries(_concat);
+
+ async.whilst = function (test, iterator, callback) {
+ if (test()) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ async.whilst(test, iterator, callback);
+ });
+ }
+ else {
+ callback();
+ }
+ };
+
+ async.doWhilst = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (test.apply(null, args)) {
+ async.doWhilst(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
+ async.until = function (test, iterator, callback) {
+ if (!test()) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ async.until(test, iterator, callback);
+ });
+ }
+ else {
+ callback();
+ }
+ };
+
+ async.doUntil = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (!test.apply(null, args)) {
+ async.doUntil(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
+ async.queue = function (worker, concurrency) {
+ if (concurrency === undefined) {
+ concurrency = 1;
+ }
+ function _insert(q, data, pos, callback) {
+ if (!q.started){
+ q.started = true;
+ }
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length == 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ if (q.drain) {
+ q.drain();
+ }
+ });
+ }
+ _each(data, function(task) {
+ var item = {
+ data: task,
+ callback: typeof callback === 'function' ? callback : null
+ };
+
+ if (pos) {
+ q.tasks.unshift(item);
+ } else {
+ q.tasks.push(item);
+ }
+
+ if (q.saturated && q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ var workers = 0;
+ var q = {
+ tasks: [],
+ concurrency: concurrency,
+ saturated: null,
+ empty: null,
+ drain: null,
+ started: false,
+ paused: false,
+ push: function (data, callback) {
+ _insert(q, data, false, callback);
+ },
+ kill: function () {
+ q.drain = null;
+ q.tasks = [];
+ },
+ unshift: function (data, callback) {
+ _insert(q, data, true, callback);
+ },
+ process: function () {
+ if (!q.paused && workers < q.concurrency && q.tasks.length) {
+ var task = q.tasks.shift();
+ if (q.empty && q.tasks.length === 0) {
+ q.empty();
+ }
+ workers += 1;
+ var next = function () {
+ workers -= 1;
+ if (task.callback) {
+ task.callback.apply(task, arguments);
+ }
+ if (q.drain && q.tasks.length + workers === 0) {
+ q.drain();
+ }
+ q.process();
+ };
+ var cb = only_once(next);
+ worker(task.data, cb);
+ }
+ },
+ length: function () {
+ return q.tasks.length;
+ },
+ running: function () {
+ return workers;
+ },
+ idle: function() {
+ return q.tasks.length + workers === 0;
+ },
+ pause: function () {
+ if (q.paused === true) { return; }
+ q.paused = true;
+ },
+ resume: function () {
+ if (q.paused === false) { return; }
+ q.paused = false;
+ // Need to call q.process once per concurrent
+ // worker to preserve full concurrency after pause
+ for (var w = 1; w <= q.concurrency; w++) {
+ async.setImmediate(q.process);
+ }
+ }
+ };
+ return q;
+ };
+
+ async.priorityQueue = function (worker, concurrency) {
+
+ function _compareTasks(a, b){
+ return a.priority - b.priority;
+ };
+
+ function _binarySearch(sequence, item, compare) {
+ var beg = -1,
+ end = sequence.length - 1;
+ while (beg < end) {
+ var mid = beg + ((end - beg + 1) >>> 1);
+ if (compare(item, sequence[mid]) >= 0) {
+ beg = mid;
+ } else {
+ end = mid - 1;
+ }
+ }
+ return beg;
+ }
+
+ function _insert(q, data, priority, callback) {
+ if (!q.started){
+ q.started = true;
+ }
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length == 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ if (q.drain) {
+ q.drain();
+ }
+ });
+ }
+ _each(data, function(task) {
+ var item = {
+ data: task,
+ priority: priority,
+ callback: typeof callback === 'function' ? callback : null
+ };
+
+ q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+ if (q.saturated && q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ // Start with a normal queue
+ var q = async.queue(worker, concurrency);
+
+ // Override push to accept second parameter representing priority
+ q.push = function (data, priority, callback) {
+ _insert(q, data, priority, callback);
+ };
+
+ // Remove unshift function
+ delete q.unshift;
+
+ return q;
+ };
+
+ async.cargo = function (worker, payload) {
+ var working = false,
+ tasks = [];
+
+ var cargo = {
+ tasks: tasks,
+ payload: payload,
+ saturated: null,
+ empty: null,
+ drain: null,
+ drained: true,
+ push: function (data, callback) {
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ _each(data, function(task) {
+ tasks.push({
+ data: task,
+ callback: typeof callback === 'function' ? callback : null
+ });
+ cargo.drained = false;
+ if (cargo.saturated && tasks.length === payload) {
+ cargo.saturated();
+ }
+ });
+ async.setImmediate(cargo.process);
+ },
+ process: function process() {
+ if (working) return;
+ if (tasks.length === 0) {
+ if(cargo.drain && !cargo.drained) cargo.drain();
+ cargo.drained = true;
+ return;
+ }
+
+ var ts = typeof payload === 'number'
+ ? tasks.splice(0, payload)
+ : tasks.splice(0, tasks.length);
+
+ var ds = _map(ts, function (task) {
+ return task.data;
+ });
+
+ if(cargo.empty) cargo.empty();
+ working = true;
+ worker(ds, function () {
+ working = false;
+
+ var args = arguments;
+ _each(ts, function (data) {
+ if (data.callback) {
+ data.callback.apply(null, args);
+ }
+ });
+
+ process();
+ });
+ },
+ length: function () {
+ return tasks.length;
+ },
+ running: function () {
+ return working;
+ }
+ };
+ return cargo;
+ };
+
+ var _console_fn = function (name) {
+ return function (fn) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ fn.apply(null, args.concat([function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (typeof console !== 'undefined') {
+ if (err) {
+ if (console.error) {
+ console.error(err);
+ }
+ }
+ else if (console[name]) {
+ _each(args, function (x) {
+ console[name](x);
+ });
+ }
+ }
+ }]));
+ };
+ };
+ async.log = _console_fn('log');
+ async.dir = _console_fn('dir');
+ /*async.info = _console_fn('info');
+ async.warn = _console_fn('warn');
+ async.error = _console_fn('error');*/
+
+ async.memoize = function (fn, hasher) {
+ var memo = {};
+ var queues = {};
+ hasher = hasher || function (x) {
+ return x;
+ };
+ var memoized = function () {
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ var key = hasher.apply(null, args);
+ if (key in memo) {
+ async.nextTick(function () {
+ callback.apply(null, memo[key]);
+ });
+ }
+ else if (key in queues) {
+ queues[key].push(callback);
+ }
+ else {
+ queues[key] = [callback];
+ fn.apply(null, args.concat([function () {
+ memo[key] = arguments;
+ var q = queues[key];
+ delete queues[key];
+ for (var i = 0, l = q.length; i < l; i++) {
+ q[i].apply(null, arguments);
+ }
+ }]));
+ }
+ };
+ memoized.memo = memo;
+ memoized.unmemoized = fn;
+ return memoized;
+ };
+
+ async.unmemoize = function (fn) {
+ return function () {
+ return (fn.unmemoized || fn).apply(null, arguments);
+ };
+ };
+
+ async.times = function (count, iterator, callback) {
+ var counter = [];
+ for (var i = 0; i < count; i++) {
+ counter.push(i);
+ }
+ return async.map(counter, iterator, callback);
+ };
+
+ async.timesSeries = function (count, iterator, callback) {
+ var counter = [];
+ for (var i = 0; i < count; i++) {
+ counter.push(i);
+ }
+ return async.mapSeries(counter, iterator, callback);
+ };
+
+ async.seq = function (/* functions... */) {
+ var fns = arguments;
+ return function () {
+ var that = this;
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ async.reduce(fns, args, function (newargs, fn, cb) {
+ fn.apply(that, newargs.concat([function () {
+ var err = arguments[0];
+ var nextargs = Array.prototype.slice.call(arguments, 1);
+ cb(err, nextargs);
+ }]))
+ },
+ function (err, results) {
+ callback.apply(that, [err].concat(results));
+ });
+ };
+ };
+
+ async.compose = function (/* functions... */) {
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ };
+
+ var _applyEach = function (eachfn, fns /*args...*/) {
+ var go = function () {
+ var that = this;
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ return eachfn(fns, function (fn, cb) {
+ fn.apply(that, args.concat([cb]));
+ },
+ callback);
+ };
+ if (arguments.length > 2) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return go.apply(this, args);
+ }
+ else {
+ return go;
+ }
+ };
+ async.applyEach = doParallel(_applyEach);
+ async.applyEachSeries = doSeries(_applyEach);
+
+ async.forever = function (fn, callback) {
+ function next(err) {
+ if (err) {
+ if (callback) {
+ return callback(err);
+ }
+ throw err;
+ }
+ fn(next);
+ }
+ next();
+ };
+
+ // Node.js
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = async;
+ }
+ // AMD / RequireJS
+ else if (typeof define !== 'undefined' && define.amd) {
+ define([], function () {
+ return async;
+ });
+ }
+ // included directly via <script> tag
+ else {
+ root.async = async;
+ }
+
+}());
--- /dev/null
+{
+ "name": "async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "main": "lib/async.js",
+ "author": {
+ "name": "Caolan McMahon"
+ },
+ "version": "0.9.2",
+ "keywords": [
+ "async",
+ "callback",
+ "utility",
+ "module"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/caolan/async.git"
+ },
+ "bugs": {
+ "url": "https://github.com/caolan/async/issues"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "nodeunit": ">0.0.0",
+ "uglify-js": "1.2.x",
+ "nodelint": ">0.0.0",
+ "lodash": ">=2.4.1"
+ },
+ "jam": {
+ "main": "lib/async.js",
+ "include": [
+ "lib/async.js",
+ "README.md",
+ "LICENSE"
+ ],
+ "categories": [
+ "Utilities"
+ ]
+ },
+ "scripts": {
+ "test": "nodeunit test/test-async.js"
+ },
+ "spm": {
+ "main": "lib/async.js"
+ },
+ "volo": {
+ "main": "lib/async.js",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+ },
+ "gitHead": "de3a16091d5125384eff4a54deb3998b13c3814c",
+ "homepage": "https://github.com/caolan/async#readme",
+ "_id": "async@0.9.2",
+ "_shasum": "aea74d5e61c1f899613bf64bda66d4c78f2fd17d",
+ "_from": "async@>=0.9.0 <0.10.0",
+ "_npmVersion": "2.9.0",
+ "_nodeVersion": "2.0.1",
+ "_npmUser": {
+ "name": "beaugunderson",
+ "email": "beau@beaugunderson.com"
+ },
+ "maintainers": [
+ {
+ "name": "caolan",
+ "email": "caolan.mcmahon@gmail.com"
+ },
+ {
+ "name": "beaugunderson",
+ "email": "beau@beaugunderson.com"
+ }
+ ],
+ "dist": {
+ "shasum": "aea74d5e61c1f899613bf64bda66d4c78f2fd17d",
+ "tarball": "http://registry.npmjs.org/async/-/async-0.9.2.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
+}
--- /dev/null
+#!/usr/bin/env node
+
+// This should probably be its own module but complaints about bower/etc.
+// support keep coming up and I'd rather just enable the workflow here for now
+// and figure out where this should live later. -- @beaugunderson
+
+var fs = require('fs');
+var _ = require('lodash');
+
+var packageJson = require('../package.json');
+
+var IGNORES = ['**/.*', 'node_modules', 'bower_components', 'test', 'tests'];
+var INCLUDES = ['lib/async.js', 'README.md', 'LICENSE'];
+var REPOSITORY_NAME = 'caolan/async';
+
+packageJson.jam = {
+ main: packageJson.main,
+ include: INCLUDES,
+ categories: ['Utilities']
+};
+
+packageJson.spm = {
+ main: packageJson.main
+};
+
+packageJson.volo = {
+ main: packageJson.main,
+ ignore: IGNORES
+};
+
+var bowerSpecific = {
+ moduleType: ['amd', 'globals', 'node'],
+ ignore: IGNORES,
+ authors: [packageJson.author]
+};
+
+var bowerInclude = ['name', 'description', 'version', 'main', 'keywords',
+ 'license', 'homepage', 'repository', 'devDependencies'];
+
+var componentSpecific = {
+ repository: REPOSITORY_NAME,
+ scripts: [packageJson.main]
+};
+
+var componentInclude = ['name', 'description', 'version', 'keywords',
+ 'license'];
+
+var bowerJson = _.merge({}, _.pick(packageJson, bowerInclude), bowerSpecific);
+var componentJson = _.merge({}, _.pick(packageJson, componentInclude), componentSpecific);
+
+fs.writeFileSync('./bower.json', JSON.stringify(bowerJson, null, 2));
+fs.writeFileSync('./component.json', JSON.stringify(componentJson, null, 2));
+fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2));
--- /dev/null
+'use strict';
+var escapeStringRegexp = require('escape-string-regexp');
+var ansiStyles = require('ansi-styles');
+var stripAnsi = require('strip-ansi');
+var hasAnsi = require('has-ansi');
+var supportsColor = require('supports-color');
+var defineProps = Object.defineProperties;
+var chalk = module.exports;
+
+function build(_styles) {
+ var builder = function builder() {
+ return applyStyle.apply(builder, arguments);
+ };
+ builder._styles = _styles;
+ // __proto__ is used because we must return a function, but there is
+ // no way to create a function with a different prototype.
+ builder.__proto__ = proto;
+ return builder;
+}
+
+var styles = (function () {
+ var ret = {};
+
+ ansiStyles.grey = ansiStyles.gray;
+
+ Object.keys(ansiStyles).forEach(function (key) {
+ ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
+
+ ret[key] = {
+ get: function () {
+ return build(this._styles.concat(key));
+ }
+ };
+ });
+
+ return ret;
+})();
+
+var proto = defineProps(function chalk() {}, styles);
+
+function applyStyle() {
+ // support varags, but simply cast to string in case there's only one arg
+ var args = arguments;
+ var argsLen = args.length;
+ var str = argsLen !== 0 && String(arguments[0]);
+ if (argsLen > 1) {
+ // don't slice `arguments`, it prevents v8 optimizations
+ for (var a = 1; a < argsLen; a++) {
+ str += ' ' + args[a];
+ }
+ }
+
+ if (!chalk.enabled || !str) {
+ return str;
+ }
+
+ /*jshint validthis: true*/
+ var nestedStyles = this._styles;
+
+ for (var i = 0; i < nestedStyles.length; i++) {
+ var code = ansiStyles[nestedStyles[i]];
+ // Replace any instances already present with a re-opening code
+ // otherwise only the part of the string until said closing code
+ // will be colored, and the rest will simply be 'plain'.
+ str = code.open + str.replace(code.closeRe, code.open) + code.close;
+ }
+
+ return str;
+}
+
+function init() {
+ var ret = {};
+
+ Object.keys(styles).forEach(function (name) {
+ ret[name] = {
+ get: function () {
+ return build([name]);
+ }
+ };
+ });
+
+ return ret;
+}
+
+defineProps(chalk, init());
+
+chalk.styles = ansiStyles;
+chalk.hasColor = hasAnsi;
+chalk.stripColor = stripAnsi;
+chalk.supportsColor = supportsColor;
+
+// detect mode if not set manually
+if (chalk.enabled === undefined) {
+ chalk.enabled = chalk.supportsColor;
+}
--- /dev/null
+../has-ansi/cli.js
\ No newline at end of file
--- /dev/null
+../strip-ansi/cli.js
\ No newline at end of file
--- /dev/null
+../supports-color/cli.js
\ No newline at end of file
--- /dev/null
+'use strict';
+var styles = module.exports;
+
+var codes = {
+ reset: [0, 0],
+
+ bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
+ dim: [2, 22],
+ italic: [3, 23],
+ underline: [4, 24],
+ inverse: [7, 27],
+ hidden: [8, 28],
+ strikethrough: [9, 29],
+
+ black: [30, 39],
+ red: [31, 39],
+ green: [32, 39],
+ yellow: [33, 39],
+ blue: [34, 39],
+ magenta: [35, 39],
+ cyan: [36, 39],
+ white: [37, 39],
+ gray: [90, 39],
+
+ bgBlack: [40, 49],
+ bgRed: [41, 49],
+ bgGreen: [42, 49],
+ bgYellow: [43, 49],
+ bgBlue: [44, 49],
+ bgMagenta: [45, 49],
+ bgCyan: [46, 49],
+ bgWhite: [47, 49]
+};
+
+Object.keys(codes).forEach(function (key) {
+ var val = codes[key];
+ var style = styles[key] = {};
+ style.open = '\u001b[' + val[0] + 'm';
+ style.close = '\u001b[' + val[1] + 'm';
+});
--- /dev/null
+{
+ "name": "ansi-styles",
+ "version": "1.1.0",
+ "description": "ANSI escape codes for styling strings in the terminal",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/ansi-styles.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-styles/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-styles",
+ "_id": "ansi-styles@1.1.0",
+ "_shasum": "eaecbf66cd706882760b2f4691582b8f55d7a7de",
+ "_from": "ansi-styles@>=1.1.0 <2.0.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "eaecbf66cd706882760b2f4691582b8f55d7a7de",
+ "tarball": "http://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# ansi-styles [](https://travis-ci.org/sindresorhus/ansi-styles)
+
+> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal
+
+You probably want the higher-level [chalk](https://github.com/sindresorhus/chalk) module for styling your strings.
+
+
+
+
+## Install
+
+```sh
+$ npm install --save ansi-styles
+```
+
+
+## Usage
+
+```js
+var ansi = require('ansi-styles');
+
+console.log(ansi.green.open + 'Hello world!' + ansi.green.close);
+```
+
+
+## API
+
+Each style has an `open` and `close` property.
+
+
+## Styles
+
+### General
+
+- `reset`
+- `bold`
+- `dim`
+- `italic` *(not widely supported)*
+- `underline`
+- `inverse`
+- `hidden`
+- `strikethrough` *(not widely supported)*
+
+### Text colors
+
+- `black`
+- `red`
+- `green`
+- `yellow`
+- `blue`
+- `magenta`
+- `cyan`
+- `white`
+- `gray`
+
+### Background colors
+
+- `bgBlack`
+- `bgRed`
+- `bgGreen`
+- `bgYellow`
+- `bgBlue`
+- `bgMagenta`
+- `bgCyan`
+- `bgWhite`
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+'use strict';
+
+var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
+
+module.exports = function (str) {
+ if (typeof str !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ return str.replace(matchOperatorsRe, '\\$&');
+};
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+{
+ "name": "escape-string-regexp",
+ "version": "1.0.3",
+ "description": "Escape RegExp special characters",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/escape-string-regexp.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "regex",
+ "regexp",
+ "re",
+ "regular",
+ "expression",
+ "escape",
+ "string",
+ "str",
+ "special",
+ "characters"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "1e446e6b4449b5f1f8868cd31bf8fd25ee37fb4b",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/escape-string-regexp/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/escape-string-regexp",
+ "_id": "escape-string-regexp@1.0.3",
+ "_shasum": "9e2d8b25bc2555c3336723750e03f099c2735bb5",
+ "_from": "escape-string-regexp@>=1.0.0 <2.0.0",
+ "_npmVersion": "2.1.16",
+ "_nodeVersion": "0.10.35",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "9e2d8b25bc2555c3336723750e03f099c2735bb5",
+ "tarball": "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# escape-string-regexp [](https://travis-ci.org/sindresorhus/escape-string-regexp)
+
+> Escape RegExp special characters
+
+
+## Install
+
+```sh
+$ npm install --save escape-string-regexp
+```
+
+
+## Usage
+
+```js
+var escapeStringRegexp = require('escape-string-regexp');
+
+var escapedString = escapeStringRegexp('how much $ for a unicorn?');
+//=> how much \$ for a unicorn\?
+
+new RegExp(escapedString);
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+#!/usr/bin/env node
+'use strict';
+var pkg = require('./package.json');
+var hasAnsi = require('./');
+var input = process.argv[2];
+
+function stdin(cb) {
+ var ret = '';
+ process.stdin.setEncoding('utf8');
+ process.stdin.on('data', function (data) {
+ ret += data;
+ });
+ process.stdin.on('end', function () {
+ cb(ret);
+ });
+}
+
+function help() {
+ console.log([
+ pkg.description,
+ '',
+ 'Usage',
+ ' $ has-ansi <string>',
+ ' $ echo <string> | has-ansi',
+ '',
+ 'Exits with code 0 if input has ANSI escape codes and 1 if not'
+ ].join('\n'));
+}
+
+function init(data) {
+ process.exit(hasAnsi(data) ? 0 : 1);
+}
+
+if (process.argv.indexOf('--help') !== -1) {
+ help();
+ return;
+}
+
+if (process.argv.indexOf('--version') !== -1) {
+ console.log(pkg.version);
+ return;
+}
+
+if (process.stdin.isTTY) {
+ if (!input) {
+ help();
+ return;
+ }
+
+ init(input);
+} else {
+ stdin(init);
+}
--- /dev/null
+'use strict';
+var ansiRegex = require('ansi-regex');
+var re = new RegExp(ansiRegex().source); // remove the `g` flag
+module.exports = re.test.bind(re);
--- /dev/null
+'use strict';
+module.exports = function () {
+ return /\u001b\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]/g;
+};
--- /dev/null
+{
+ "name": "ansi-regex",
+ "version": "0.2.1",
+ "description": "Regular expression for matching ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/ansi-regex.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-regex/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "_id": "ansi-regex@0.2.1",
+ "_shasum": "0d8e946967a3d8143f93e24e298525fc1b2235f9",
+ "_from": "ansi-regex@>=0.2.0 <0.3.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "0d8e946967a3d8143f93e24e298525fc1b2235f9",
+ "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# ansi-regex [](https://travis-ci.org/sindresorhus/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save ansi-regex
+```
+
+
+## Usage
+
+```js
+var ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001b[4mcake\u001b[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001b[4mcake\u001b[0m'.match(ansiRegex());
+//=> ['\u001b[4m', '\u001b[0m']
+```
+
+*It's a function so you can create multiple instances. Regexes with the global flag will have the `.lastIndex` property changed for each call to methods on the instance. Therefore reusing the instance with multiple calls will not work as expected for `.test()`.*
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+{
+ "name": "has-ansi",
+ "version": "0.1.0",
+ "description": "Check if a string has ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/has-ansi.git"
+ },
+ "bin": {
+ "has-ansi": "cli.js"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js",
+ "cli.js"
+ ],
+ "keywords": [
+ "cli",
+ "bin",
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "string",
+ "tty",
+ "escape",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern",
+ "has"
+ ],
+ "dependencies": {
+ "ansi-regex": "^0.2.0"
+ },
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/has-ansi/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/has-ansi",
+ "_id": "has-ansi@0.1.0",
+ "_shasum": "84f265aae8c0e6a88a12d7022894b7568894c62e",
+ "_from": "has-ansi@>=0.1.0 <0.2.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "84f265aae8c0e6a88a12d7022894b7568894c62e",
+ "tarball": "http://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# has-ansi [](https://travis-ci.org/sindresorhus/has-ansi)
+
+> Check if a string has [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save has-ansi
+```
+
+
+## Usage
+
+```js
+var hasAnsi = require('has-ansi');
+
+hasAnsi('\u001b[4mcake\u001b[0m');
+//=> true
+
+hasAnsi('cake');
+//=> false
+```
+
+
+## CLI
+
+```sh
+$ npm install --global has-ansi
+```
+
+```
+$ has-ansi --help
+
+Usage
+ $ has-ansi <string>
+ $ echo <string> | has-ansi
+
+Exits with code 0 if input has ANSI escape codes and 1 if not
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+#!/usr/bin/env node
+'use strict';
+var fs = require('fs');
+var pkg = require('./package.json');
+var strip = require('./');
+var input = process.argv[2];
+
+function help() {
+ console.log([
+ pkg.description,
+ '',
+ 'Usage',
+ ' $ strip-ansi <input-file> > <output-file>',
+ ' $ cat <input-file> | strip-ansi > <output-file>',
+ '',
+ 'Example',
+ ' $ strip-ansi unicorn.txt > unicorn-stripped.txt'
+ ].join('\n'));
+}
+
+if (process.argv.indexOf('--help') !== -1) {
+ help();
+ return;
+}
+
+if (process.argv.indexOf('--version') !== -1) {
+ console.log(pkg.version);
+ return;
+}
+
+if (input) {
+ process.stdout.write(strip(fs.readFileSync(input, 'utf8')));
+ return;
+}
+
+process.stdin.setEncoding('utf8');
+process.stdin.on('data', function (data) {
+ process.stdout.write(strip(data));
+});
--- /dev/null
+'use strict';
+var ansiRegex = require('ansi-regex')();
+
+module.exports = function (str) {
+ return typeof str === 'string' ? str.replace(ansiRegex, '') : str;
+};
--- /dev/null
+'use strict';
+module.exports = function () {
+ return /\u001b\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]/g;
+};
--- /dev/null
+{
+ "name": "ansi-regex",
+ "version": "0.2.1",
+ "description": "Regular expression for matching ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/ansi-regex.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-regex/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "_id": "ansi-regex@0.2.1",
+ "_shasum": "0d8e946967a3d8143f93e24e298525fc1b2235f9",
+ "_from": "ansi-regex@>=0.2.0 <0.3.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "0d8e946967a3d8143f93e24e298525fc1b2235f9",
+ "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# ansi-regex [](https://travis-ci.org/sindresorhus/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save ansi-regex
+```
+
+
+## Usage
+
+```js
+var ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001b[4mcake\u001b[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001b[4mcake\u001b[0m'.match(ansiRegex());
+//=> ['\u001b[4m', '\u001b[0m']
+```
+
+*It's a function so you can create multiple instances. Regexes with the global flag will have the `.lastIndex` property changed for each call to methods on the instance. Therefore reusing the instance with multiple calls will not work as expected for `.test()`.*
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+{
+ "name": "strip-ansi",
+ "version": "0.3.0",
+ "description": "Strip ANSI escape codes",
+ "license": "MIT",
+ "bin": {
+ "strip-ansi": "cli.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/strip-ansi.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js",
+ "cli.js"
+ ],
+ "keywords": [
+ "strip",
+ "trim",
+ "remove",
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "dependencies": {
+ "ansi-regex": "^0.2.1"
+ },
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/strip-ansi/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/strip-ansi",
+ "_id": "strip-ansi@0.3.0",
+ "_shasum": "25f48ea22ca79187f3174a4db8759347bb126220",
+ "_from": "strip-ansi@>=0.3.0 <0.4.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "25f48ea22ca79187f3174a4db8759347bb126220",
+ "tarball": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# strip-ansi [](https://travis-ci.org/sindresorhus/strip-ansi)
+
+> Strip [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save strip-ansi
+```
+
+
+## Usage
+
+```js
+var stripAnsi = require('strip-ansi');
+
+stripAnsi('\x1b[4mcake\x1b[0m');
+//=> 'cake'
+```
+
+
+## CLI
+
+```sh
+$ npm install --global strip-ansi
+```
+
+```sh
+$ strip-ansi --help
+
+Usage
+ $ strip-ansi <input-file> > <output-file>
+ $ cat <input-file> | strip-ansi > <output-file>
+
+Example
+ $ strip-ansi unicorn.txt > unicorn-stripped.txt
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+#!/usr/bin/env node
+'use strict';
+var pkg = require('./package.json');
+var supportsColor = require('./');
+var input = process.argv[2];
+
+function help() {
+ console.log([
+ pkg.description,
+ '',
+ 'Usage',
+ ' $ supports-color',
+ '',
+ 'Exits with code 0 if color is supported and 1 if not'
+ ].join('\n'));
+}
+
+if (!input || process.argv.indexOf('--help') !== -1) {
+ help();
+ return;
+}
+
+if (process.argv.indexOf('--version') !== -1) {
+ console.log(pkg.version);
+ return;
+}
+
+process.exit(supportsColor ? 0 : 1);
--- /dev/null
+'use strict';
+module.exports = (function () {
+ if (process.argv.indexOf('--no-color') !== -1) {
+ return false;
+ }
+
+ if (process.argv.indexOf('--color') !== -1) {
+ return true;
+ }
+
+ if (process.stdout && !process.stdout.isTTY) {
+ return false;
+ }
+
+ if (process.platform === 'win32') {
+ return true;
+ }
+
+ if ('COLORTERM' in process.env) {
+ return true;
+ }
+
+ if (process.env.TERM === 'dumb') {
+ return false;
+ }
+
+ if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
+ return true;
+ }
+
+ return false;
+})();
--- /dev/null
+{
+ "name": "supports-color",
+ "version": "0.2.0",
+ "description": "Detect whether a terminal supports color",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/supports-color.git"
+ },
+ "bin": {
+ "supports-color": "cli.js"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js",
+ "cli.js"
+ ],
+ "keywords": [
+ "cli",
+ "bin",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "ansi",
+ "styles",
+ "tty",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "support",
+ "supports",
+ "capability",
+ "detect"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/supports-color/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/supports-color",
+ "_id": "supports-color@0.2.0",
+ "_shasum": "d92de2694eb3f67323973d7ae3d8b55b4c22190a",
+ "_from": "supports-color@>=0.2.0 <0.3.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "d92de2694eb3f67323973d7ae3d8b55b4c22190a",
+ "tarball": "http://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# supports-color [](https://travis-ci.org/sindresorhus/supports-color)
+
+> Detect whether a terminal supports color
+
+
+## Install
+
+```sh
+$ npm install --save supports-color
+```
+
+
+## Usage
+
+```js
+var supportsColor = require('supports-color');
+
+if (supportsColor) {
+ console.log('Terminal supports color');
+}
+```
+
+It obeys the `--color` and `--no-color` CLI flags.
+
+
+## CLI
+
+```sh
+$ npm install --global supports-color
+```
+
+```sh
+$ supports-color --help
+
+Usage
+ $ supports-color
+
+# Exits with code 0 if color is supported and 1 if not
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+{
+ "name": "chalk",
+ "version": "0.5.1",
+ "description": "Terminal string styling done right. Created because the `colors` module does some really horrible things.",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sindresorhus/chalk.git"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha",
+ "bench": "matcha benchmark.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "ansi",
+ "styles",
+ "tty",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "dependencies": {
+ "ansi-styles": "^1.1.0",
+ "escape-string-regexp": "^1.0.0",
+ "has-ansi": "^0.1.0",
+ "strip-ansi": "^0.3.0",
+ "supports-color": "^0.2.0"
+ },
+ "devDependencies": {
+ "matcha": "^0.5.0",
+ "mocha": "*"
+ },
+ "gitHead": "994758f01293f1fdcf63282e9917cb9f2cfbdaac",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/chalk/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/chalk",
+ "_id": "chalk@0.5.1",
+ "_shasum": "663b3a648b68b55d04690d49167aa837858f2174",
+ "_from": "chalk@>=0.5.1 <0.6.0",
+ "_npmVersion": "1.4.14",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "663b3a648b68b55d04690d49167aa837858f2174",
+ "tarball": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# <img width="300" src="https://cdn.rawgit.com/sindresorhus/chalk/77ae94f63ab1ac61389b190e5a59866569d1a376/logo.svg" alt="chalk">
+
+> Terminal string styling done right
+
+[](https://travis-ci.org/sindresorhus/chalk)
+
+
+[colors.js](https://github.com/Marak/colors.js) is currently the most popular string styling module, but it has serious deficiencies like extending String.prototype which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough.
+
+**Chalk is a clean and focused alternative.**
+
+
+
+
+## Why
+
+- Highly performant
+- Doesn't extend String.prototype
+- Expressive API
+- Ability to nest styles
+- Clean and focused
+- Auto-detects color support
+- Actively maintained
+- [Used by 1000+ modules](https://npmjs.org/browse/depended/chalk)
+
+
+## Install
+
+```sh
+$ npm install --save chalk
+```
+
+
+## Usage
+
+Chalk comes with an easy to use composable API where you just chain and nest the styles you want.
+
+```js
+var chalk = require('chalk');
+
+// style a string
+console.log( chalk.blue('Hello world!') );
+
+// combine styled and normal strings
+console.log( chalk.blue('Hello'), 'World' + chalk.red('!') );
+
+// compose multiple styles using the chainable API
+console.log( chalk.blue.bgRed.bold('Hello world!') );
+
+// pass in multiple arguments
+console.log( chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz') );
+
+// nest styles
+console.log( chalk.red('Hello', chalk.underline.bgBlue('world') + '!') );
+
+// nest styles of the same type even (color, underline, background)
+console.log( chalk.green('I am a green line ' + chalk.blue('with a blue substring') + ' that becomes green again!') );
+```
+
+Easily define your own themes.
+
+```js
+var chalk = require('chalk');
+var error = chalk.bold.red;
+console.log(error('Error!'));
+```
+
+Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data).
+
+```js
+var name = 'Sindre';
+console.log(chalk.green('Hello %s'), name);
+//=> Hello Sindre
+```
+
+
+## API
+
+### chalk.`<style>[.<style>...](string, [string...])`
+
+Example: `chalk.red.bold.underline('Hello', 'world');`
+
+Chain [styles](#styles) and call the last one as a method with a string argument. Order doesn't matter.
+
+Multiple arguments will be separated by space.
+
+### chalk.enabled
+
+Color support is automatically detected, but you can override it.
+
+### chalk.supportsColor
+
+Detect whether the terminal [supports color](https://github.com/sindresorhus/supports-color).
+
+Can be overridden by the user with the flags `--color` and `--no-color`.
+
+Used internally and handled for you, but exposed for convenience.
+
+### chalk.styles
+
+Exposes the styles as [ANSI escape codes](https://github.com/sindresorhus/ansi-styles).
+
+Generally not useful, but you might need just the `.open` or `.close` escape code if you're mixing externally styled strings with yours.
+
+```js
+var chalk = require('chalk');
+
+console.log(chalk.styles.red);
+//=> {open: '\u001b[31m', close: '\u001b[39m'}
+
+console.log(chalk.styles.red.open + 'Hello' + chalk.styles.red.close);
+```
+
+### chalk.hasColor(string)
+
+Check whether a string [has color](https://github.com/sindresorhus/has-ansi).
+
+### chalk.stripColor(string)
+
+[Strip color](https://github.com/sindresorhus/strip-ansi) from a string.
+
+Can be useful in combination with `.supportsColor` to strip color on externally styled text when it's not supported.
+
+Example:
+
+```js
+var chalk = require('chalk');
+var styledString = getText();
+
+if (!chalk.supportsColor) {
+ styledString = chalk.stripColor(styledString);
+}
+```
+
+
+## Styles
+
+### General
+
+- `reset`
+- `bold`
+- `dim`
+- `italic` *(not widely supported)*
+- `underline`
+- `inverse`
+- `hidden`
+- `strikethrough` *(not widely supported)*
+
+### Text colors
+
+- `black`
+- `red`
+- `green`
+- `yellow`
+- `blue`
+- `magenta`
+- `cyan`
+- `white`
+- `gray`
+
+### Background colors
+
+- `bgBlack`
+- `bgRed`
+- `bgGreen`
+- `bgYellow`
+- `bgBlue`
+- `bgMagenta`
+- `bgCyan`
+- `bgWhite`
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+less.js
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="ProjectCodeStyleSettingsManager">\r
+ <option name="PER_PROJECT_SETTINGS">\r
+ <value>\r
+ <XML>\r
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />\r
+ </XML>\r
+ </value>\r
+ </option>\r
+ </component>\r
+</project>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />\r
+</project>\r
+\r
--- /dev/null
+<component name="InspectionProjectProfileManager">\r
+ <profile version="1.0" is_locked="false">\r
+ <option name="myName" value="Project Default" />\r
+ <option name="myLocal" value="false" />\r
+ <inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />\r
+ <inspection_tool class="Jscs" enabled="true" level="ERROR" enabled_by_default="true" />\r
+ </profile>\r
+</component>
\ No newline at end of file
--- /dev/null
+<component name="InspectionProjectProfileManager">\r
+ <settings>\r
+ <option name="PROJECT_PROFILE" value="Project Default" />\r
+ <option name="USE_PROJECT_PROFILE" value="true" />\r
+ <version value="1.0" />\r
+ </settings>\r
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="JavaScriptLibraryMappings">\r
+ <file url="PROJECT" libraries="{Node.js v0.8.4 Core Modules}" />\r
+ </component>\r
+</project>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="JscsConfiguration" jscs-preset="" />
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="JSHintConfiguration" version="1.0.0" use-config-file="true">\r
+ <option asi="false" />\r
+ <option bitwise="true" />\r
+ <option boss="false" />\r
+ <option browser="false" />\r
+ <option camelcase="false" />\r
+ <option couch="false" />\r
+ <option curly="true" />\r
+ <option debug="false" />\r
+ <option devel="false" />\r
+ <option dojo="false" />\r
+ <option eqeqeq="true" />\r
+ <option eqnull="false" />\r
+ <option es3="false" />\r
+ <option esnext="false" />\r
+ <option evil="false" />\r
+ <option expr="false" />\r
+ <option forin="true" />\r
+ <option freeze="false" />\r
+ <option funcscope="false" />\r
+ <option gcl="false" />\r
+ <option globalstrict="false" />\r
+ <option immed="false" />\r
+ <option iterator="false" />\r
+ <option jquery="false" />\r
+ <option lastsemic="false" />\r
+ <option latedef="false" />\r
+ <option laxbreak="false" />\r
+ <option laxcomma="false" />\r
+ <option loopfunc="false" />\r
+ <option moz="false" />\r
+ <option noarg="true" />\r
+ <option noempty="true" />\r
+ <option nomen="false" />\r
+ <option nonew="true" />\r
+ <option node="true" />\r
+ <option maxerr="200" />\r
+ <option mootools="false" />\r
+ <option multistr="false" />\r
+ <option newcap="false" />\r
+ <option nonbsp="false" />\r
+ <option nonstandard="false" />\r
+ <option notypeof="false" />\r
+ <option noyield="false" />\r
+ <option onevar="false" />\r
+ <option passfail="false" />\r
+ <option phantom="false" />\r
+ <option plusplus="false" />\r
+ <option proto="false" />\r
+ <option prototypejs="false" />\r
+ <option quotmark="false" />\r
+ <option rhino="false" />\r
+ <option scripturl="false" />\r
+ <option shadow="false" />\r
+ <option smarttabs="false" />\r
+ <option strict="false" />\r
+ <option sub="false" />\r
+ <option supernew="false" />\r
+ <option trailing="false" />\r
+ <option undef="true" />\r
+ <option unused="false" />\r
+ <option validthis="false" />\r
+ <option white="false" />\r
+ <option worker="false" />\r
+ <option wsh="false" />\r
+ <option yui="false" />\r
+ </component>\r
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module type="WEB_MODULE" version="4">\r
+ <component name="NewModuleRootManager">\r
+ <content url="file://$MODULE_DIR$">\r
+ <excludeFolder url="file://$MODULE_DIR$/.grunt" />\r
+ <excludeFolder url="file://$MODULE_DIR$/dist" />\r
+ <excludeFolder url="file://$MODULE_DIR$/node_modules" />\r
+ <excludeFolder url="file://$MODULE_DIR$/projectFilesBackup" />\r
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />\r
+ </content>\r
+ <orderEntry type="inheritedJdk" />\r
+ <orderEntry type="sourceFolder" forTests="false" />\r
+ <orderEntry type="library" name="Node.js v0.8.4 Core Modules" level="application" />\r
+ </component>\r
+</module>\r
+\r
--- /dev/null
+<component name="libraryTable">\r
+ <library name="sass-stdlib">\r
+ <CLASSES />\r
+ <SOURCES>\r
+ <root url="file://$APPLICATION_HOME_DIR$/plugins/sass/lib/stubs/sass_functions.scss" />\r
+ </SOURCES>\r
+ </library>\r
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="ProjectRootManager" version="2" />\r
+</project>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="ProjectModuleManager">\r
+ <modules>\r
+ <module fileurl="file://$PROJECT_DIR$/.idea/less.js.iml" filepath="$PROJECT_DIR$/.idea/less.js.iml" />\r
+ </modules>\r
+ </component>\r
+</project>\r
+\r
--- /dev/null
+<component name="DependencyValidationManager">\r
+ <state>\r
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />\r
+ </state>\r
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="VcsDirectoryMappings">\r
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />\r
+ </component>\r
+</project>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project version="4">\r
+ <component name="ChangeListManager">\r
+ <list default="true" id="50832ad7-0bb2-445d-8bdb-45c8a0f7f27b" name="Default" comment="">\r
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/CHANGELOG.md" afterPath="$PROJECT_DIR$/CHANGELOG.md" />\r
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lib/less/index.js" afterPath="$PROJECT_DIR$/lib/less/index.js" />\r
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/package.json" afterPath="$PROJECT_DIR$/package.json" />\r
+ </list>\r
+ <ignored path="less.js.iws" />\r
+ <ignored path=".idea/workspace.xml" />\r
+ <ignored path="$PROJECT_DIR$/.grunt/" />\r
+ <ignored path="$PROJECT_DIR$/dist/" />\r
+ <ignored path="$PROJECT_DIR$/node_modules/" />\r
+ <ignored path="$PROJECT_DIR$/projectFilesBackup/" />\r
+ <ignored path="$PROJECT_DIR$/tmp/" />\r
+ <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />\r
+ <option name="TRACKING_ENABLED" value="true" />\r
+ <option name="SHOW_DIALOG" value="false" />\r
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />\r
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />\r
+ <option name="LAST_RESOLUTION" value="IGNORE" />\r
+ </component>\r
+ <component name="ChangesViewManager" flattened_view="true" show_ignored="false" />\r
+ <component name="CreatePatchCommitExecutor">\r
+ <option name="PATCH_PATH" value="" />\r
+ </component>\r
+ <component name="DaemonCodeAnalyzer">\r
+ <disable_hints />\r
+ </component>\r
+ <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />\r
+ <component name="FavoritesManager">\r
+ <favorites_list name="less.js" />\r
+ </component>\r
+ <component name="FileEditorManager">\r
+ <leaf>\r
+ <file leaf-file-name="types.js" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/lib/less/functions/types.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1259" max-vertical-offset="1700">\r
+ <caret line="82" column="27" selection-start-line="82" selection-start-column="27" selection-end-line="82" selection-end-column="27" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="functions.less" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/test/less/functions.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="3017" max-vertical-offset="3740">\r
+ <caret line="212" column="28" selection-start-line="212" selection-start-column="28" selection-end-line="212" selection-end-column="28" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name=".travis.yml" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/.travis.yml">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="34" max-vertical-offset="306">\r
+ <caret line="2" column="2" selection-start-line="2" selection-start-column="2" selection-end-line="2" selection-end-column="2" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="appveyor.yml" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/appveyor.yml">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="323" max-vertical-offset="748">\r
+ <caret line="19" column="10" selection-start-line="19" selection-start-column="10" selection-end-line="19" selection-end-column="10" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="CHANGELOG.md" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/CHANGELOG.md">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-5.44" vertical-offset="0" max-vertical-offset="8602">\r
+ <caret line="8" column="56" selection-start-line="8" selection-start-column="56" selection-end-line="8" selection-end-column="56" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="package.json" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/package.json">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-1.3076923" vertical-offset="0" max-vertical-offset="1683">\r
+ <caret line="2" column="19" selection-start-line="2" selection-start-column="19" selection-end-line="2" selection-end-column="19" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="index.js" pinned="false" current-in-tab="true">\r
+ <entry file="file://$PROJECT_DIR$/lib/less/index.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.17435898" vertical-offset="0" max-vertical-offset="595">\r
+ <caret line="4" column="25" selection-start-line="4" selection-start-column="25" selection-end-line="4" selection-end-column="25" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="utils.js" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/lib/less/utils.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="340" max-vertical-offset="442">\r
+ <caret line="20" column="0" selection-start-line="20" selection-start-column="0" selection-end-line="20" selection-end-column="0" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="utils.js" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/lib/less-browser/utils.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="119" max-vertical-offset="510">\r
+ <caret line="7" column="56" selection-start-line="7" selection-start-column="56" selection-end-line="7" selection-end-column="56" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ <file leaf-file-name="functions.css" pinned="false" current-in-tab="false">\r
+ <entry file="file://$PROJECT_DIR$/test/css/functions.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="2772" max-vertical-offset="3247">\r
+ <caret line="183" column="2" selection-start-line="183" selection-start-column="2" selection-end-line="183" selection-end-column="2" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </file>\r
+ </leaf>\r
+ </component>\r
+ <component name="FindManager">\r
+ <FindUsagesManager>\r
+ <setting name="OPEN_NEW_TAB" value="false" />\r
+ </FindUsagesManager>\r
+ </component>\r
+ <component name="Git.Settings">\r
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />\r
+ </component>\r
+ <component name="GitLogSettings">\r
+ <option name="myDateState">\r
+ <MyDateState />\r
+ </option>\r
+ </component>\r
+ <component name="IdeDocumentHistory">\r
+ <option name="CHANGED_PATHS">\r
+ <list>\r
+ <option value="$PROJECT_DIR$/lib/less/tree/color.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/visitors/extend-visitor.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/visitors/import-visitor.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/mixin-definition.js" />\r
+ <option value="$PROJECT_DIR$/lib/less-browser/cache.js" />\r
+ <option value="$PROJECT_DIR$/lib/less-node/url-file-manager.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/anonymous.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/plugin-manager.js" />\r
+ <option value="$PROJECT_DIR$/.jscsrc" />\r
+ <option value="$PROJECT_DIR$/bin/lessc" />\r
+ <option value="$PROJECT_DIR$/lib/less/visitors/to-css-visitor.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/rule.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/quoted.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/media.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/extend.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/dimension.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/source-map-output.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/parse-tree.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/import-manager.js" />\r
+ <option value="$PROJECT_DIR$/lib/less-rhino/index.js" />\r
+ <option value="$PROJECT_DIR$/lib/less-node/file-manager.js" />\r
+ <option value="$PROJECT_DIR$/lib/less-browser/index.js" />\r
+ <option value="$PROJECT_DIR$/lib/less-browser/file-manager.js" />\r
+ <option value="$PROJECT_DIR$/test/modify-vars.js" />\r
+ <option value="$PROJECT_DIR$/test/copy-bom.js" />\r
+ <option value="$PROJECT_DIR$/test/browser/runner-main-spec.js" />\r
+ <option value="$PROJECT_DIR$/test/browser/runner-main-options.js" />\r
+ <option value="$PROJECT_DIR$/test/browser/runner-global-vars-spec.js" />\r
+ <option value="$PROJECT_DIR$/test/browser/runner-errors-spec.js" />\r
+ <option value="$PROJECT_DIR$/test/browser/runner-browser-options.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/parser/parser.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/tree/ruleset.js" />\r
+ <option value="$PROJECT_DIR$/test/less/urls.less" />\r
+ <option value="$PROJECT_DIR$/test/less/import/imports/logo.less" />\r
+ <option value="$PROJECT_DIR$/lib/less/environment/abstract-file-manager.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/contexts.js" />\r
+ <option value="$PROJECT_DIR$/test/css/urls.css" />\r
+ <option value="$PROJECT_DIR$/test/css/static-urls/urls.css" />\r
+ <option value="$PROJECT_DIR$/test/css/import-interpolation.css" />\r
+ <option value="$PROJECT_DIR$/test/browser/less/relative-urls/urls.less" />\r
+ <option value="$PROJECT_DIR$/test/browser/css/relative-urls/urls.css" />\r
+ <option value="$PROJECT_DIR$/test/less-test.js" />\r
+ <option value="$PROJECT_DIR$/lib/less/functions/types.js" />\r
+ <option value="$PROJECT_DIR$/test/css/functions.css" />\r
+ <option value="$PROJECT_DIR$/test/less/functions.less" />\r
+ <option value="$PROJECT_DIR$/.travis.yml" />\r
+ <option value="$PROJECT_DIR$/lib/less-browser/utils.js" />\r
+ <option value="$PROJECT_DIR$/appveyor.yml" />\r
+ <option value="$PROJECT_DIR$/CHANGELOG.md" />\r
+ <option value="$PROJECT_DIR$/package.json" />\r
+ <option value="$PROJECT_DIR$/lib/less/index.js" />\r
+ </list>\r
+ </option>\r
+ </component>\r
+ <component name="JsGruntFileManager">\r
+ <gruntfiles>\r
+ <GruntfileState>\r
+ <gruntfile-path>$PROJECT_DIR$/Gruntfile.js</gruntfile-path>\r
+ </GruntfileState>\r
+ </gruntfiles>\r
+ <initial>false</initial>\r
+ </component>\r
+ <component name="JsGulpfileManager">\r
+ <detection-done>true</detection-done>\r
+ </component>\r
+ <component name="ProjectFrameBounds">\r
+ <option name="x" value="54" />\r
+ <option name="y" value="-8" />\r
+ <option name="width" value="1320" />\r
+ <option name="height" value="784" />\r
+ </component>\r
+ <component name="ProjectLevelVcsManager" settingsEditedManually="false">\r
+ <OptionsSetting value="true" id="Add" />\r
+ <OptionsSetting value="true" id="Remove" />\r
+ <OptionsSetting value="true" id="Checkout" />\r
+ <OptionsSetting value="true" id="Update" />\r
+ <OptionsSetting value="true" id="Status" />\r
+ <OptionsSetting value="true" id="Edit" />\r
+ <ConfirmationsSetting value="0" id="Add" />\r
+ <ConfirmationsSetting value="0" id="Remove" />\r
+ </component>\r
+ <component name="ProjectReloadState">\r
+ <option name="STATE" value="0" />\r
+ </component>\r
+ <component name="ProjectView">\r
+ <navigator currentView="ProjectPane" proportions="" version="1">\r
+ <flattenPackages />\r
+ <showMembers />\r
+ <showModules />\r
+ <showLibraryContents ProjectPane="true" />\r
+ <hideEmptyPackages />\r
+ <abbreviatePackageNames />\r
+ <autoscrollToSource />\r
+ <autoscrollFromSource />\r
+ <sortByType />\r
+ </navigator>\r
+ <panes>\r
+ <pane id="ProjectPane">\r
+ <subPane>\r
+ <PATH>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />\r
+ </PATH_ELEMENT>\r
+ </PATH>\r
+ <PATH>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />\r
+ </PATH_ELEMENT>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />\r
+ </PATH_ELEMENT>\r
+ </PATH>\r
+ <PATH>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />\r
+ </PATH_ELEMENT>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />\r
+ </PATH_ELEMENT>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="lib" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />\r
+ </PATH_ELEMENT>\r
+ </PATH>\r
+ <PATH>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />\r
+ </PATH_ELEMENT>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less.js" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />\r
+ </PATH_ELEMENT>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="lib" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />\r
+ </PATH_ELEMENT>\r
+ <PATH_ELEMENT>\r
+ <option name="myItemId" value="less" />\r
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />\r
+ </PATH_ELEMENT>\r
+ </PATH>\r
+ </subPane>\r
+ </pane>\r
+ <pane id="Scope" />\r
+ </panes>\r
+ </component>\r
+ <component name="PropertiesComponent">\r
+ <property name="options.splitter.main.proportions" value="0.3" />\r
+ <property name="WebServerToolWindowFactoryState" value="false" />\r
+ <property name="options.lastSelected" value="preferences.editor" />\r
+ <property name="last_opened_file_path" value="$PROJECT_DIR$" />\r
+ <property name="FullScreen" value="false" />\r
+ <property name="GoToClass.includeJavaFiles" value="false" />\r
+ <property name="options.searchVisible" value="true" />\r
+ <property name="options.splitter.details.proportions" value="0.2" />\r
+ <property name="HbShouldOpenHtmlAsHb" value="" />\r
+ <property name="js.eslint.nodeInterpreter" value="C:\Program Files (x86)\nodejs\node.exe" />\r
+ <property name="js.eslint.eslintPackage" value="" />\r
+ </component>\r
+ <component name="RecentsManager">\r
+ <key name="CopyFile.RECENT_KEYS">\r
+ <recent name="C:\git\less.js\test\less\errors" />\r
+ <recent name="C:\git\less.js\test\browser" />\r
+ <recent name="C:\git\less.js\lib\less\environment" />\r
+ <recent name="C:\git\less.js\lib\less" />\r
+ <recent name="C:\git\less.js\lib\less-node" />\r
+ </key>\r
+ <key name="MoveFile.RECENT_KEYS">\r
+ <recent name="C:\git\less.js\lib\less-node" />\r
+ <recent name="C:\git\less.js\lib\less" />\r
+ <recent name="C:\git\less.js\lib\less-rhino" />\r
+ <recent name="C:\git\less.js\lib\less\visitor" />\r
+ <recent name="C:\git\less.js\lib\less-browser" />\r
+ </key>\r
+ </component>\r
+ <component name="RunManager">\r
+ <configuration default="true" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application">\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="DartUnitRunConfigurationType" factoryName="DartUnit">\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="JSTestDriver:ConfigurationType" factoryName="JsTestDriver">\r
+ <setting name="configLocationType" value="CONFIG_FILE" />\r
+ <setting name="settingsFile" value="" />\r
+ <setting name="serverType" value="INTERNAL" />\r
+ <setting name="preferredDebugBrowser" value="Chrome" />\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="JavaScriptTestRunnerKarma" factoryName="Karma" config-file="">\r
+ <envs />\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="NodeJSConfigurationType" factoryName="Node.js" working-dir="$PROJECT_DIR$">\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="cucumber.js" factoryName="Cucumber.js">\r
+ <option name="cucumberJsArguments" value="" />\r
+ <option name="executablePath" />\r
+ <option name="filePath" />\r
+ <method />\r
+ </configuration>\r
+ <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js">\r
+ <node-options />\r
+ <gulpfile />\r
+ <tasks />\r
+ <arguments />\r
+ <pass-parent-envs>true</pass-parent-envs>\r
+ <envs />\r
+ <method />\r
+ </configuration>\r
+ <list size="0" />\r
+ </component>\r
+ <component name="ShelveChangesManager" show_recycled="false" />\r
+ <component name="SvnConfiguration" myUseAcceleration="nothing">\r
+ <configuration />\r
+ </component>\r
+ <component name="TaskManager">\r
+ <task active="true" id="Default" summary="Default task">\r
+ <changelist id="50832ad7-0bb2-445d-8bdb-45c8a0f7f27b" name="Default" comment="" />\r
+ <created>1357400236487</created>\r
+ <option name="number" value="Default" />\r
+ <updated>1357400236487</updated>\r
+ </task>\r
+ <servers />\r
+ </component>\r
+ <component name="ToolWindowManager">\r
+ <frame x="54" y="-8" width="1320" height="784" extended-state="6" />\r
+ <editor active="true" />\r
+ <layout>\r
+ <window_info id="Grunt" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />\r
+ <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />\r
+ <window_info id="Application Servers" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.23466258" sideWeight="0.6714976" order="2" side_tool="false" content_ui="combo" />\r
+ <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" />\r
+ <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.3846154" sideWeight="0.5" order="9" side_tool="true" content_ui="tabs" />\r
+ <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.3846154" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />\r
+ <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />\r
+ <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />\r
+ </layout>\r
+ </component>\r
+ <component name="Vcs.Log.UiProperties">\r
+ <option name="RECENTLY_FILTERED_USER_GROUPS">\r
+ <collection />\r
+ </option>\r
+ <option name="RECENTLY_FILTERED_BRANCH_GROUPS">\r
+ <collection />\r
+ </option>\r
+ </component>\r
+ <component name="VcsContentAnnotationSettings">\r
+ <option name="myLimit" value="2678400000" />\r
+ </component>\r
+ <component name="VcsManagerConfiguration">\r
+ <option name="myTodoPanelSettings">\r
+ <TodoPanelSettings />\r
+ </option>\r
+ </component>\r
+ <component name="XDebuggerManager">\r
+ <breakpoint-manager>\r
+ <option name="time" value="1" />\r
+ </breakpoint-manager>\r
+ <watches-manager />\r
+ </component>\r
+ <component name="editorHistoryManager">\r
+ <entry file="file://$PROJECT_DIR$/lib/less/tree/extend.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="57" max-vertical-offset="969">\r
+ <caret line="17" column="12" selection-start-line="17" selection-start-column="12" selection-end-line="17" selection-end-column="12" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/tree/dimension.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1099" max-vertical-offset="2737">\r
+ <caret line="74" column="20" selection-start-line="74" selection-start-column="20" selection-end-line="74" selection-end-column="20" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/parse-tree.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1122">\r
+ <caret line="15" column="22" selection-start-line="15" selection-start-column="22" selection-end-line="15" selection-end-column="22" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/import-manager.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="629" max-vertical-offset="2244">\r
+ <caret line="49" column="13" selection-start-line="49" selection-start-column="13" selection-end-line="49" selection-end-column="13" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less-rhino/index.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="567" max-vertical-offset="7684">\r
+ <caret line="41" column="5" selection-start-line="41" selection-start-column="5" selection-end-line="41" selection-end-column="5" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less-browser/index.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-5.9615383" vertical-offset="2939" max-vertical-offset="4454">\r
+ <caret line="182" column="30" selection-start-line="182" selection-start-column="30" selection-end-line="182" selection-end-column="30" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less-browser/file-manager.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1680" max-vertical-offset="2125">\r
+ <caret line="117" column="0" selection-start-line="117" selection-start-column="0" selection-end-line="117" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/modify-vars.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="425">\r
+ <caret line="17" column="4" selection-start-line="17" selection-start-column="4" selection-end-line="17" selection-end-column="4" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/copy-bom.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="868" max-vertical-offset="1326">\r
+ <caret line="72" column="0" selection-start-line="72" selection-start-column="0" selection-end-line="72" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-main-spec.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="221">\r
+ <caret line="4" column="8" selection-start-line="4" selection-start-column="8" selection-end-line="4" selection-end-column="8" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-main-options.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="391">\r
+ <caret line="15" column="6" selection-start-line="15" selection-start-column="6" selection-end-line="15" selection-end-column="6" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-global-vars-spec.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153">\r
+ <caret line="1" column="4" selection-start-line="1" selection-start-column="4" selection-end-line="1" selection-end-column="4" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-errors-spec.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="153">\r
+ <caret line="1" column="4" selection-start-line="1" selection-start-column="4" selection-end-line="1" selection-end-column="4" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-legacy-spec.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-browser-spec.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-errors-options.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-console-errors.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/runner-browser-options.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="511" max-vertical-offset="969">\r
+ <caret line="49" column="4" selection-start-line="49" selection-start-column="4" selection-end-line="49" selection-end-column="4" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/source-map-builder.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-0.0" vertical-offset="0" max-vertical-offset="1207">\r
+ <caret line="0" column="27" selection-start-line="0" selection-start-column="27" selection-end-line="0" selection-end-column="27" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/source-map-output.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1815" max-vertical-offset="2465">\r
+ <caret line="115" column="13" selection-start-line="115" selection-start-column="13" selection-end-line="115" selection-end-column="13" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/tree/ruleset.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1564" max-vertical-offset="12155">\r
+ <caret line="104" column="26" selection-start-line="104" selection-start-column="26" selection-end-line="104" selection-end-column="26" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/parser/parser.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-7.1153846" vertical-offset="26777" max-vertical-offset="28101">\r
+ <caret line="1586" column="10" selection-start-line="1586" selection-start-column="10" selection-end-line="1586" selection-end-column="10" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less-node/file-manager.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="295" max-vertical-offset="1938">\r
+ <caret line="25" column="42" selection-start-line="25" selection-start-column="42" selection-end-line="25" selection-end-column="42" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/less/import/imports/font.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/less/import/imports/logo.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-1.3333334" vertical-offset="0" max-vertical-offset="204">\r
+ <caret line="4" column="33" selection-start-line="4" selection-start-column="33" selection-end-line="4" selection-end-column="33" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/environment/abstract-file-manager.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="380" max-vertical-offset="2125">\r
+ <caret line="28" column="2" selection-start-line="28" selection-start-column="2" selection-end-line="28" selection-end-column="2" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/tree/url.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="188" max-vertical-offset="1020">\r
+ <caret line="27" column="30" selection-start-line="27" selection-start-column="30" selection-end-line="27" selection-end-column="30" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/contexts.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1196" max-vertical-offset="1972">\r
+ <caret line="79" column="30" selection-start-line="79" selection-start-column="30" selection-end-line="79" selection-end-column="30" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/less/import/urls.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/css/urls.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-9.076923" vertical-offset="461" max-vertical-offset="1581">\r
+ <caret line="41" column="0" selection-start-line="41" selection-start-column="0" selection-end-line="41" selection-end-column="33" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/css/import-interpolation.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.26153848" vertical-offset="0" max-vertical-offset="390">\r
+ <caret line="6" column="40" selection-start-line="6" selection-start-column="40" selection-end-line="6" selection-end-column="40" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/css/url-args/urls.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.42032966" vertical-offset="0" max-vertical-offset="1054">\r
+ <caret line="9" column="7" selection-start-line="9" selection-start-column="7" selection-end-line="9" selection-end-column="7" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/css/static-urls/urls.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-7.269231" vertical-offset="355" max-vertical-offset="884">\r
+ <caret line="32" column="37" selection-start-line="32" selection-start-column="37" selection-end-line="32" selection-end-column="37" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/less/urls.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-5.72549" vertical-offset="1306" max-vertical-offset="1700">\r
+ <caret line="94" column="0" selection-start-line="94" selection-start-column="0" selection-end-line="94" selection-end-column="0" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/less/relative-urls/urls.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-4.0" vertical-offset="0" max-vertical-offset="680">\r
+ <caret line="12" column="33" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="33" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/browser/css/relative-urls/urls.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="714">\r
+ <caret line="8" column="12" selection-start-line="8" selection-start-column="12" selection-end-line="8" selection-end-column="12" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/index.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-4.6538463" vertical-offset="202" max-vertical-offset="1003">\r
+ <caret line="19" column="25" selection-start-line="19" selection-start-column="25" selection-end-line="19" selection-end-column="25" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/less-test.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-10.384615" vertical-offset="495" max-vertical-offset="6511">\r
+ <caret line="45" column="9" selection-start-line="45" selection-start-column="9" selection-end-line="45" selection-end-column="9" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/functions/data-uri.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="303" max-vertical-offset="1547">\r
+ <caret line="32" column="14" selection-start-line="32" selection-start-column="14" selection-end-line="32" selection-end-column="14" />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/functions/function-caller.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="391">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/functions/index.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="425">\r
+ <caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/functions/types.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="1259" max-vertical-offset="1700">\r
+ <caret line="82" column="27" selection-start-line="82" selection-start-column="27" selection-end-line="82" selection-end-column="27" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/css/functions.css">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="2772" max-vertical-offset="3247">\r
+ <caret line="183" column="2" selection-start-line="183" selection-start-column="2" selection-end-line="183" selection-end-column="2" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/test/less/functions.less">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="3017" max-vertical-offset="3740">\r
+ <caret line="212" column="28" selection-start-line="212" selection-start-column="28" selection-end-line="212" selection-end-column="28" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/.travis.yml">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="306">\r
+ <caret line="2" column="2" selection-start-line="2" selection-start-column="2" selection-end-line="2" selection-end-column="2" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/utils.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="442">\r
+ <caret line="20" column="0" selection-start-line="20" selection-start-column="0" selection-end-line="20" selection-end-column="0" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less-browser/utils.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="510">\r
+ <caret line="7" column="56" selection-start-line="7" selection-start-column="56" selection-end-line="7" selection-end-column="56" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/appveyor.yml">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="748">\r
+ <caret line="19" column="10" selection-start-line="19" selection-start-column="10" selection-end-line="19" selection-end-column="10" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/CHANGELOG.md">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-5.44" vertical-offset="0" max-vertical-offset="8602">\r
+ <caret line="8" column="56" selection-start-line="8" selection-start-column="56" selection-end-line="8" selection-end-column="56" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/package.json">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="-1.3076923" vertical-offset="0" max-vertical-offset="1683">\r
+ <caret line="2" column="19" selection-start-line="2" selection-start-column="19" selection-end-line="2" selection-end-column="19" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ <entry file="file://$PROJECT_DIR$/lib/less/index.js">\r
+ <provider selected="true" editor-type-id="text-editor">\r
+ <state vertical-scroll-proportion="0.17435898" vertical-offset="0" max-vertical-offset="595">\r
+ <caret line="4" column="25" selection-start-line="4" selection-start-column="25" selection-end-line="4" selection-end-column="25" />\r
+ <folding />\r
+ </state>\r
+ </provider>\r
+ </entry>\r
+ </component>\r
+</project>
\ No newline at end of file
--- /dev/null
+{\r
+ "disallowImplicitTypeConversion": ["numeric", "boolean", "binary", "string"],\r
+ "disallowKeywords": ["with"],\r
+ "disallowMixedSpacesAndTabs": true,\r
+ "disallowMultipleLineBreaks": true,\r
+ "disallowOperatorBeforeLineBreak": ["."],\r
+ "disallowSpaceAfterKeywords": [\r
+ "void"//,\r
+ //"typeof"\r
+ ],\r
+ "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~"],\r
+ "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],\r
+ "disallowSpacesInCallExpression": true,\r
+ "disallowSpacesInNamedFunctionExpression": {\r
+ "beforeOpeningRoundBrace": true},\r
+ "disallowTrailingComma": true,\r
+ "disallowTrailingWhitespace": true,\r
+ "maximumLineLength": 160,\r
+ "requireCommaBeforeLineBreak": true,\r
+ "requireCurlyBraces": [ "if",\r
+ "else",\r
+ "for",\r
+ "while",\r
+ "do",\r
+ "try",\r
+ "catch"],\r
+ "requireOperatorBeforeLineBreak": [ "?",\r
+ "=",\r
+ "+",\r
+ "-",\r
+ "/",\r
+ "*",\r
+ "==",\r
+ "===",\r
+ "!=",\r
+ "!==",\r
+ ">",\r
+ ">=",\r
+ "<",\r
+ "<="],\r
+ "requireSpaceAfterBinaryOperators": true,\r
+ "requireSpaceAfterKeywords": [\r
+ "else",\r
+ "case",\r
+ "try",\r
+ "typeof",\r
+ "return",\r
+ "if",\r
+ "for",\r
+ "while",\r
+ "do"\r
+ ],\r
+ "requireSpaceBeforeBlockStatements": true,\r
+ "requireSpaceBeforeBinaryOperators": [\r
+ "=",\r
+ "+",\r
+ "-",\r
+ "/",\r
+ "*",\r
+ "==",\r
+ "===",\r
+ "!=",\r
+ "!=="\r
+ ],\r
+ "requireSpaceBeforeBlockStatements": true,\r
+ "requireSpaceBetweenArguments": true,\r
+ "requireSpacesInConditionalExpression": true,\r
+ "requireSpacesInForStatement": true,\r
+ "requireSpacesInNamedFunctionExpression": {\r
+ "beforeOpeningCurlyBrace": true\r
+ },\r
+ "validateIndentation": 4\r
+}
\ No newline at end of file
--- /dev/null
+{
+ "evil": true,
+ "latedef": true,
+ "node": true,
+ "undef": true,
+ "unused": "vars",
+ "noarg": true,
+ "eqnull": true,
+ "forin": true,
+ "predef": ["Promise"]
+}
--- /dev/null
+.gitattributes
+build/
+.grunt/
+benchmark/
+test/
+# re-include test files as they can be useful for plugins that do testing
+!test/*.js
+tmp/
+gradle/
--- /dev/null
+language: node_js
+node_js:
+ - "0.12"
+ - "0.11"
+ - "0.10"
+install:
+ - npm install -g grunt-cli
+ - npm install
+env:
+ global:
+ - secure: TrNVruWYaUK5ALga1y7wRY+MLjWJECUSCsBmKW5EUmIevOUxqHWu7M89FANKxstEeFRRAGH3QJbloRxnzIgh0U0ah5npE9XA1bYXGO5khoXeIyk7pNRfjIo8aEnJH1Vp8vWA6J6ovxdJ7lCFKEGvGKxGde50knVl7KFVVULlX2U=
+ - secure: Rzh+CEI7YRvvVkOruPE8Z0dkU0s13V6b6cpqbN72vxbJl/Jm5PUZkjTFJdkWJrW3ErhCKX6EC7XdGvrclqEA9WAqKzrecqCJYqTnw4MwqiAj6F9wqE/BqhoWg4xPxm0MK/7eJMvLCgjNpe+gc1CaeFJZkLSNWn6nOFke+vVlf9Q=
--- /dev/null
+# 2.4.0
+
+2015-02-07
+
+ - Support for plugins that pre-process (to add libraries silently etc.)
+ - Empty sourcemaps now work
+ - Extract and Length functions now ignore comments in a list (more work to come to fix the general problem)
+ - fragment url's are treated absolute since they refer to the html document
+ - Extends on a selector joined with `&` now work better
+ - Nested mixins work better with !important (regression in 2.3.0)
+ - The promise dependency is now actually optional (introduced in 2.0.0)
+ - Files with just `\r` newlines now process ok (regression in 2.0.0)
+ - When strict units is off and the unit is 1/x, (e.g. 1 / 12px) the unit output is x, previously nothing (regression in 2.0.0)
+
+# 2.3.1
+
+2015-01-28
+
+ - Fix depends option (regression in 2.3.0)
+ - Support parent selector (`&`) used in sub element expression (e.g. `:not(.c_&)`)
+
+# 2.3.0
+
+2015-01-27
+
+ - add isruleset function
+ - add optional import option, causing less to not fail if file not found
+ - Fix browsers-side cache.
+ - Many fixes to import reference - support `@support` and keyframe
+ - Selectors now interpolate pseudo selectors (e.g. `:@{hover}`)
+ - Fix comments missed off if they were at the end of the file
+ - Fix !important used with parametric mixins
+ - Emits warnings for extends when the target is not found
+ - include-path now works on data-uri
+ - variables and function calls work for path in data-uri
+ - Fix absolute paths not working on imports sometimes.
+ - Unicode BOM removed again
+ - Misc. bug fixes
+
+# 2.2.0
+
+2015-01-04
+
+ - do not apply relative paths to svg-gradient and data-uri functions data-uri output
+ - using import filename interpolation and import inline together now works
+ - deprecate the compression option (still works, but outputs a warning unless silent)
+ - The node version of less now has image-size, image-width, image-height which return the image dimensions of a file
+ - Fixed an issue that could cause the parse to occur more than once and the callback be called multiple times
+ - if you are outputting to the console, lessc defaults to silent so warnings do not end up in output
+ - isunit function supports '' to test if a dimension has no unit
+ - data-uri function now counts characters after base64 encoding instead of bytes before encoding to determine ie8 support
+ - fix bug effecting guards on pseudo class selectors
+ - do not cache on the browser when used with modifyVars
+ - detection if less does not parse last character in file
+ - detection of whether a file is css now requires /css, .css, ?css, &css instead of just css. You can still tell less the type of file using import options.
+ - remove extra new line added to sourcemap entry inline file
+ - support safari extension
+ - less.parse now exposes a way to get the AST. We do not recommend you use this unless you need to.
+
+# 2.1.2
+
+2014-12-20
+
+ - Fix for use with requirejs
+ - Fixes for data-uri function
+
+# 2.1.1
+
+2014-11-27
+
+ - Improved keyword and anonymous usage with the replace function
+ - Added getCSSAppendage to sourcemap builder to avoid duplication in plugins
+ - Fix problem with plugins when used with the promises version of render
+ - If the render callback throws an exception it now propogates instead of calling the callback again with an error
+
+# 2.1.0
+
+2014-11-23
+
+ - Fixed isSync option, it was using sync file operations but promises are guaranteed to call back async. We now support promises as a feature rather than the 1st class way of doing things.
+ - Browser code is now synchronous again, like in v1, meaning it blocks the site until less is compiled
+ - Some fixes for variable imports which affected filemanagers when synchronous
+ - Fixed lessc makefile dependencies option
+ - output now reports back a imports field with an array of imported files
+ - relative path test for drive names (so windows only) is now case insensitive
+ - Fix for IE7 - use getChar instead of indexing array
+ - variables using !important now output !important, which bubbles up to affect the rule
+ - livereload cache buster is now treated specially
+ - upgrade dependencies
+
+# 2.0.0
+
+2014-11-09
+
+ - Fixed multiplication in non strict units mode to take the left operand unit, in the case that the unit cannot be resolved
+ - Some fixes for browser cross-compatibility
+ - browser tests now pass in IE 8-11 and FF
+ - added index.js and browser.js in root as shortcuts
+ - fixed some local variable spellings
+ - support for @counter-style directive
+
+# 2.0.0-b3
+
+2014-11-01
+
+ - some refactoring of browser structure to allow use of api vs normal browser bundle
+ - browser bundle no longer leaks require
+ - browser can now be scoped with just window
+ - browser useFileCache defaults to true, but file cache is now cleared when refreshing or in watch mode
+
+# 2.0.0-b2
+
+2014-10-26
+
+ - Imports are now sequenced and so are consistent (previously some complex projects might end up with occasional different orderings)
+ - Imports with variables are better supported - variables can be specified in sub imports
+ - Support for rebeccapurple
+ - Browser can now accept options as attributes on the script tag and the link tags e.g. `<script data-verbose="false" src="less.js"...`
+ - adding a .less file extension is done in the abstract file manager so it the behaviour can be overridden by certain file managers
+ - Fixed a bug where unquoted urls beginning `//` e.g. `url(//file/file.less)` would be incorrectly interpreted (bug introduced in b-1)
+ - lessc plugins can be a function, used as a constructor as well as an object - this to allow the plugin more flexibility to be used programattically
+
+# 2.0.0-b1
+
+2014-10-19
+
+ - Public Beta / Release Candidate - Feedback welcome
+ For a guide to breaking changes see [the v2 upgrade guide](http://lesscss.org/usage/#v2-upgrade-guide)
+ - no longer including old versions of less in the repo or npm
+ - not including test less and gradle files in npm
+ - colours now output in the format they are added, so yellow will output yellow, not its hex counterpart
+ - better parsing - better comment support and comments in brackets can now contain comments including quotes.
+ - Removal of dependency on clean-css - install less-plugin-clean-css and use --clean-css to reference plugin
+ - Environment Support - less is now separate from its node and browser environment implementations and adding support for another javascript environment should be straight forward.
+ - Plugin Support - it is now straight forward to add AST manipulations (see less-plugin-inline-images), file managers (see less-plugin-npm-import) and post processors (see less-plugin-clean-css and less-plugin-autoprefix).
+ - We now recommend using less.render and using the parser directly is not in the same way as in v2. It is possible but it would require changes and we do not guarantee it will not be broken in minor version releases.
+ - In the browser, less.pageLoadFinished will be a promise, resolved when less has finished its initial processing. less.refresh and less.modifyVars also return promises.
+ - In the browser, as before less is used as options, however this is now copied to less.options if you need to access after less has run
+ - In the browser, the cache can be overwritten by setting less.cache before less loads. After load less.cache will be the default implementation.
+ - less.js now uses browserify to generate its browser side component
+ - default values for the sourcemap options have been re-done and improved to hopefully mean creating sourcemaps is easier
+ - Many smaller bugfixes and API changes. Please let us know if something you relied on has disappeared or an area should be better documented.
+
+# 1.7.5
+
+2014-09-03
+
+ - Allow comments in keyframe (complete comment support coming in 2.0)
+ - pass options to parser from less.render
+ - Support /deep/ combinator
+ - handle fragments in data-uri's
+ - float @charsets to the top correctly
+ - updates to some dependencies
+ - Fix interpolated import in media query
+ - A few other various small corrections
+
+# 1.7.4
+
+2014-07-27
+
+ - Handle uppercase paths in browser
+ - Show error if an empty selector is used in extend
+ - Fix property merging in directives
+ - Fix ordering of charset and import directives
+ - Fix race condition that caused a rules is undefined error sometimes if you had a complex import strategy
+ - Better error message for imports missing semi-colons or malformed
+ - Do not use util.print to avoid deprecate warnings in node 0.11
+
+# 1.7.3
+
+2014-06-22
+
+ - Include dist files, missing from 1.7.2
+ - Do not round the results of color functions, like lightness, hue, luma etc.
+ - Support cover and contain keywords in background definitions
+
+ # 1.7.2
+
+2014-06-19
+
+ - Allow paths option to be a string (in 1.7.1 less started throwing an exception instead of incorrectly processing the string as an array of chars)
+ - Do not round numbers when used with javascript (introduced 1.7.0)
+
+# 1.7.1
+
+2014-06-08
+
+ - Fix detection of recursive mixins
+ - Fix the paths option for later versions of node (0.10+)
+ - Fix paths joining bug
+ - Fix a number precision issue on some versions of node
+ - Fix an IE8 issue with importing css files
+ - Fix IE11 detection for xhr requests
+ - Modify var works if the last line of a less file is a comment.
+ - Better detection of valid hex colour codes
+ - Some stability fixes to support a low number of available file handles
+ - Support comparing values with different quote types e.g. "test" now === 'test'
+ - Give better error messages if accessing a url that returns a non 200 status code
+ - Fix the e() function when passed empty string
+ - Several minor bug fixes
+
+# 1.7.0
+
+2014-02-27
+
+ - Add support for rulesets in variables and passed to mixins to allow wrapping
+ - Change luma to follow the w3c spec, luma is available as luminance. Contrast still uses luma so you may see differences if your threshold % is close to the existing calculated luma.
+ - Upgraded clean css which means the --selectors-merge-mode is now renamed --compatibility
+ - Add support for using variables with @keyframes, @namespace, @charset
+ - Support property merging with +_ when spaces are needed and keep + for comma separated
+ - Imports now always import once consistently - a race condition meant previously certain configurations would lead to a different ordering of files
+ - Fix support for `.mixin(@args...)` when called with no args (e.g. `.mixin();`)
+ - Do unit conversions with min and max functions. Don't pass through if not understood, throw an error
+ - Allow % to be passed on its own to the unit function e.g. `unit(10, %)`
+ - Fix a bug when comparing a unit value to a non-unit value if the unit-value was the multiple of another unit (e.g. cm, mm, deg etc.)
+ - Fix mixins with media queries in import reference files not being put into the output (they now output, they used to incorrectly not)
+ - Fix lint mode - now reports all errors
+ - Fixed a small scope issue with & {} selector rulesets incorrectly making mixins visible - regression from 1.6.2
+ - Browser - added log level "debug" at 3 to get less logging, The default has changed so unless you set the value to the default you won't see a difference
+ - Browser - logLevel takes effect regardless of the environment (production/dev)
+ - Browser - added postProcessor option, a function called to post-process the css before adding to the page
+ - Browser - use the right request for file access in IE
+
+# 1.6.3
+
+2014-02-08
+
+ - Fix issue with calling toCSS twice not working in some situations (like with bootstrap 2)
+
+# 1.6.2
+
+2014-02-02
+
+ - The Rhino release is fixed!
+ - ability to use uppercase colours
+ - Fix a nasty bug causing syntax errors when selector interpolation is preceded by a long comment (and some other cases)
+ - Fix a major bug with the variable scope in guards on selectors (e.g. not mixins)
+ - Fold in `& when () {` to the current selector rather than duplicating it
+ - fix another issue with array prototypes
+ - add a url-args option which adds a value to all urls (for cache busting)
+ - Round numbers to 8 decimal places - thereby stopping javascript precision errors
+ - some improvements to the default() function in more complex scenarios
+ - improved missing '{' and '(' detection
+
+# 1.6.1
+
+2014-01-12
+
+ - support ^ and ^^ shadow dom selectors
+ - fix sourcemap selector (used to report end of the element or selector) and directive position (previously not supported)
+ - fix parsing empty less files
+ - error on (currently) ambiguous guards on multiple css selectors
+ - older environments - protect against typeof regex returning function
+ - Do not use default keyword
+ - use innerHTML in tests, not innerText
+ - protect for-in in case Array and Object prototypes have custom fields
+
+# 1.6.0
+
+2014-01-01
+
+ - Properties can be interpolated, e.g. @{prefix}-property: value;
+ - a default function has been added only valid in mixin definitions to determine if no other mixins have been matched
+ - Added a plugins option that allows specifying an array of visitors run on the less AST
+ - Performance improvements that may result in approx 20-40% speed up
+ - Javascript evaluations returning numbers can now be used in calculations/functions
+ - fixed issue when adding colours, taking the alpha over 1 and breaking when used in colour functions
+ - when adding together 2 colours with non zero alpha, the alpha will now be combined rather than added
+ - the advanced colour functions no longer ignore transparency, they blend that too
+ - Added --clean-option and cleancssOptions to allow passing in clean css options
+ - rgba declarations are now always clamped e.g. rgba(-1,258,258, -1) becomes rgba(0, 255, 255, 0)
+ - Fix possible issue with import reference not bringing in styles (may not be a bugfix, just a code tidy)
+ - Fix some issues with urls() being prefixed twice and unquoted urls in mixins being processed each time they are called
+ - Fixed error messages for undefined variables in javascript evaluation
+ - Fixed line/column numbers from math errors
+
+# 1.5.1
+
+2013-11-17
+
+ - Added source-map-URL option
+ - Fixed a bug which meant the minimised 1.5.0 browser version was not wrapped, meaning it interfered with require js
+ - Fixed a bug where the browser version assume port was specified
+ - Added the ability to specify variables on the command line
+ - Upgraded clean-css and fixed it from trying to import
+ - correct a bug meaning imports weren't synchronous (syncImport option available for full synchronous behaviour)
+ - better mixin matching behaviour with calling multiple classes e.g. .a.b.c;
+
+# 1.5.0
+
+2013-10-21
+
+ - sourcemap support
+ - support for import inline option to include css that you do NOT want less to parse e.g. `@import (inline) "file.css";`
+ - better support for modifyVars (refresh styles with new variables, using a file cache), is now more resiliant
+ - support for import reference option to reference external css, but not output it. Any mixin calls or extend's will be output.
+ - support for guards on selectors (currently only if you have a single selector)
+ - allow property merging through the +: syntax
+ - Added min/max functions
+ - Added length function and improved extract to work with comma separated values
+ - when using import multiple, sub imports are imported multiple times into final output
+ - fix bad spaces between namespace operators
+ - do not compress comment if it begins with an exclamation mark
+ - Fix the saturate function to pass through when using the CSS syntax
+ - Added svg-gradient function
+ - Added no-js option to lessc (in browser, use javascriptEnabled: false) which disallows JavaScript in less files
+ - switched from the little supported and buggy cssmin (previously ycssmin) to clean-css
+ - support transparent as a color, but not convert between rgba(0, 0, 0, 0) and transparent
+ - remove sys.puts calls to stop deprecation warnings in future node.js releases
+ - Browser: added logLevel option to control logging (2 = everything, 1 = errors only, 0 = no logging)
+ - Browser: added errorReporting option which can be "html" (default) or "console" or a function
+ - Now uses grunt for building and testing
+ - A few bug fixes for media queries, extends, scoping, compression and import once.
+
+# 1.4.2
+
+2013-07-20
+
+ - if you don't pass a strict maths option, font size/line height options are output correctly again
+ - npmignore now include .gitattributes
+ - property names may include capital letters
+ - various windows path fixes (capital letters, multiple // in a path)
+
+# 1.4.1
+
+2013-07-05
+
+ - fix syncImports and yui-compress option, as they were being ignored
+ - fixed several global variable leaks
+ - handle getting null or undefined passed as the options object
+
+# 1.4.0
+
+2013-06-05
+
+ - fix passing of strict maths option
+
+# 1.4.0 Beta 4
+
+2013-05-04
+
+ - change strictMaths to strictMath. Enable this with --strict-math=on in lessc and strictMath:true in JavaScript.
+ - change lessc option for strict units to --strict-units=off
+
+# 1.4.0 Beta 3
+
+2013-04-30
+
+ - strictUnits now defaults to false and the true case now gives more useful but less correct results, e.g. 2px/1px = 2px
+ - Process ./ when having relative paths
+ - add isunit function for mixin guards and non basic units
+ - extends recognise attributes
+ - exception errors extend the JavaScript Error
+ - remove es-5-shim as standard from the browser
+ - Fix path issues with windows/linux local paths
+
+# 1.4.0 Beta 1 & 2
+
+2013-03-07
+
+ - support for `:extend()` in selectors (e.g. `input:extend(.button) {}`) and `&:extend();` in ruleset (e.g. `input { &:extend(.button all); }`)
+ - maths is now only done inside brackets. This means font: statements, media queries and the calc function can use a simpler format without being escaped. Disable this with --strict-maths-off in lessc and strictMaths:false in JavaScript.
+ - units are calculated, e.g. 200cm+1m = 3m, 3px/1px = 3. If you use units inconsistently you will get an error. Suppress this error with --strict-units-off in lessc or strictUnits:false in JavaScript
+ - `(~"@var")` selector interpolation is removed. Use @{var} in selectors to have variable selectors
+ - default behaviour of import is to import each file once. `@import-once` has been removed.
+ - You can specify options on imports to force it to behave as css or less `@import (less) "file.css"` will process the file as less
+ - variables in mixins no longer 'leak' into their calling scope
+ - added data-uri function which will inline an image into the output css. If ieCompat option is true and file is too large, it will fallback to a url()
+ - significant bug fixes to our debug options
+ - other parameters can be used as defaults in mixins e.g. .a(@a, @b:@a)
+ - an error is shown if properties are used outside of a ruleset
+ - added extract function which picks a value out of a list, e.g. extract(12 13 14, 3) => 14
+ - added luma, hsvhue, hsvsaturation, hsvvalue functions
+ - added pow, pi, mod, tan, sin, cos, atan, asin, acos and sqrt math functions
+ - added convert function, e.g. convert(1rad, deg) => value in degrees
+ - lessc makes output directories if they don't exist
+ - lessc `@import` supports https and 301's
+ - lessc "-depends" option for lessc writes out the list of import files used in makefile format
+ - lessc "-lint" option just reports errors
+ - support for namespaces in attributes and selector interpolation in attributes
+ - other bug fixes
+
+# 1.3.3
+
+2012-12-30
+
+ - Fix critical bug with mixin call if using multiple brackets
+ - when using the filter contrast function, the function is passed through if the first argument is not a color
+
+# 1.3.2
+
+2012-12-28
+
+ - browser and server url re-writing is now aligned to not re-write (previous lessc behaviour)
+ - url-rewriting can be made to re-write to be relative to the entry file using the relative-urls option (less.relativeUrls option)
+ - rootpath option can be used to add a base path to every url
+ - Support mixin argument separator of ';' so you can pass comma separated values. e.g. `.mixin(23px, 12px;);`
+ - Fix lots of problems with named arguments in corner cases, not behaving as expected
+ - hsv, hsva, unit functions
+ - fixed lots more bad error messages
+ - fix `@import-once` to use the full path, not the relative one for determining if an import has been imported already
+ - support `:not(:nth-child(3))`
+ - mixin guards take units into account
+ - support unicode descriptors (`U+00A1-00A9`)
+ - support calling mixins with a stack when using `&` (broken in 1.3.1)
+ - support `@namespace` and namespace combinators
+ - when using % with colour functions, take into account a colour is out of 256
+ - when doing maths with a % do not divide by 100 and keep the unit
+ - allow url to contain % (e.g. %20 for a space)
+ - if a mixin guard stops execution a default mixin is not required
+ - units are output in strings (use the unit function if you need to get the value without unit)
+ - do not infinite recurse when mixins call mixins of the same name
+ - fix issue on important on mixin calls
+ - fix issue with multiple comments being confused
+ - tolerate multiple semi-colons on rules
+ - ignore subsequant `@charset`
+ - syncImport option for node.js to read files syncronously
+ - write the output directory if it is missing
+ - change dependency on cssmin to ycssmin
+ - lessc can load files over http
+ - allow calling less.watch() in non dev mode
+ - don't cache in dev mode
+ - less files cope with query parameters better
+ - sass debug statements are now chrome compatible
+ - modifyVars function added to re-render with different root variables
+
+# 1.3.1
+
+2012-10-18
+
+- Support for comment and @media debugging statements
+- bug fix for async access in chrome extensions
+- new functions tint, shade, multiply, screen, overlay, hardlight, difference, exclusion, average, negation, softlight, red, green, blue, contrast
+- allow escaped characters in attributes
+- in selectors support @{a} directly, e.g. .a.@{a} { color: black; }
+- add fraction parameter to round function
+- much better support for & selector
+- preserve order of link statements client side
+- lessc has better help
+- rhino version fixed
+- fix bugs in clientside error handling
+- support dpi, vmin, vm, dppx, dpcm units
+- Fix ratios in media statements
+- in mixin guards allow comparing colors and strings
+- support for -*-keyframes (for -khtml but now supports any)
+- in mix function, default weight to 50%
+- support @import-once
+- remove duplicate rules in output
+- implement named parameters when calling mixins
+- many numerous bug fixes
+
+# 1.3.0
+
+2012-03-10
+
+- @media bubbling
+- Support arbitrary entities as selectors
+- [Variadic argument support](https://gist.github.com/1933613)
+- Behaviour of zero-arity mixins has [changed](https://gist.github.com/1933613)
+- Allow `@import` directives in any selector
+- Media-query features can now be a variable
+- Automatic merging of media-query conditions
+- Fix global variable leaks
+- Fix error message on wrong-arity call
+- Fix an `@arguments` behaviour bug
+- Fix `::` selector output
+- Fix a bug when using @media with mixins
+
+
+# 1.2.1
+
+2012-01-15
+
+- Fix imports in browser
+- Improve error reporting in browser
+- Fix Runtime error reports from imported files
+- Fix `File not found` import error reporting
+
+
+# 1.2.0
+
+2012-01-07
+
+- Mixin guards
+- New function `percentage`
+- New `color` function to parse hex color strings
+- New type-checking stylesheet functions
+- Fix Rhino support
+- Fix bug in string arguments to mixin call
+- Fix error reporting when index is 0
+- Fix browser support in WebKit and IE
+- Fix string interpolation bug when var is empty
+- Support `!important` after mixin calls
+- Support vanilla @keyframes directive
+- Support variables in certain css selectors, like `nth-child`
+- Support @media and @import features properly
+- Improve @import support with media features
+- Improve error reports from imported files
+- Improve function call error reporting
+- Improve error-reporting
--- /dev/null
+# Contributing to Less.js
+
+> We welcome feature requests and bug reports. Please read these guidelines before submitting one.
+
+
+<span class="warning">**Words that begin with the at sign (`@`) must be wrapped in backticks!** </span>. As a courtesy to avoid sending notifications to any user that might have the `@username` being referenced, please remember that GitHub usernames also start with the at sign. If you don't wrap them in backticks, users will get unintended notifications from you.
+
+GitHub has other great markdown features as well, [go here to learn more about them](https://help.github.com/articles/github-flavored-markdown).
+
+
+## Reporting Issues
+
+We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems that we can fix within the Less.js core. Please read the following guidelines before opening any issue.
+
+1. **Search for existing issues.** We get a lot of duplicate issues, and you'd help us out a lot by first checking if someone else has reported the same issue. Moreover, the issue may have already been resolved with a fix available.
+2. **Create an isolated and reproducible test case.** Be sure the problem exists in Less.js's code with [reduced test cases](http://css-tricks.com/reduced-test-cases/) that should be included in each bug report.
+3. **Test with the latest version**. We get a lot of issues that could be resolved by updating your version of Less.js.
+3. **Include an example with source.** E.g. You can use [less2css.org](http://less2css.org/) to create a short test case.
+4. **Share as much information as possible.** Include operating system and version. Describe how you use Less. If you use it in the browser, please include browser and version, and the version of Less.js you're using. Let us know if you're using the command line (`lessc`) or an external tool. And try to include steps to reproduce the bug.
+5. If you have a solution or suggestion for how to fix the bug you're reporting, please include it, or make a pull request - don't assume the maintainers know how to fix it just because you do.
+
+Please report documentation issues in [the documentation project](https://github.com/less/less-docs).
+
+## Feature Requests
+
+* Please search for existing feature requests first to see if something similar already exists.
+* Include a clear and specific use-case. We love new ideas, but we do not add language features without a reason.
+* Consider whether or not your language feature would be better as a function or implemented in a 3rd-party build system such as [assemble-less](http://github.com/assemble/assemble-less).
+
+
+## Pull Requests
+
+_Pull requests are encouraged!_
+
+* Start by adding a feature request to get feedback and see how your idea is received.
+* If your pull request solves an existing issue, but it's different in some way, _please create a new issue_ and make sure to discuss it with the core contributors. Otherwise you risk your hard work being rejected.
+* Do not change the **./dist/** folder, we do this when releasing
+* _Please add tests_ for your work. Tests are invoked using `grunt test` command. It will run both node.js tests and browser ([PhantomJS](http://phantomjs.org/)) tests.
+
+### Coding Standards
+
+* Always use spaces, never tabs
+* End lines in semi-colons.
+* Loosely aim towards jsHint standards
+
+
+## Developing
+If you want to take an issue just add a small comment saying you are having a go at something, so we don't get duplication.
+
+Learn more about [developing Less.js](http://lesscss.org/usage/#developing-less).
--- /dev/null
+'use strict';
+var fs = require('fs');
+
+module.exports = function (grunt) {
+
+ // Report the elapsed execution time of tasks.
+ require('time-grunt')(grunt);
+
+ var COMPRESS_FOR_TESTS = true;
+
+ // Project configuration.
+ grunt.initConfig({
+
+ // Metadata required for build.
+ build: grunt.file.readYAML('build/build.yml'),
+ pkg: grunt.file.readJSON('package.json'),
+ meta: {
+ license: '<%= _.pluck(pkg.licenses, "type").join(", ") %>',
+ copyright: 'Copyright (c) 2009-<%= grunt.template.today("yyyy") %>',
+ banner: '/*!\n' +
+ ' * Less - <%= pkg.description %> v<%= pkg.version %>\n' +
+ ' * http://lesscss.org\n' +
+ ' *\n' +
+ ' * <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>>\n' +
+ ' * Licensed under the <%= meta.license %> License.\n' +
+ ' *\n' +
+ ' */\n\n' +
+ ' /**' +
+ ' * @license <%= meta.license %>\n' +
+ ' */\n\n'
+ },
+
+ shell: {
+ options: {stdout: true, failOnError: true},
+ test: {
+ command: 'node test'
+ },
+ benchmark: {
+ command: 'node benchmark/less-benchmark.js'
+ },
+ "sourcemap-test": {
+ command: [
+ 'node bin/lessc --source-map=test/sourcemaps/maps/import-map.map test/less/import.less test/sourcemaps/import.css',
+ 'node bin/lessc --source-map test/less/sourcemaps/basic.less test/sourcemaps/basic.css'
+ ].join('&&')
+ }
+ },
+
+ browserify: {
+ browser: {
+ src: ['./lib/less-browser/bootstrap.js'],
+ options: {
+ exclude: ["promise"],
+ require: ["promise/polyfill.js"],
+ browserifyOptions: {
+ standalone: 'less'
+ }
+ },
+ dest: 'tmp/less.js'
+ }
+ },
+ concat: {
+ options: {
+ stripBanners: 'all',
+ banner: '<%= meta.banner %>'
+ },
+ browsertest: {
+ src: COMPRESS_FOR_TESTS ? '<%= uglify.test.dest %>' : '<%= browserify.browser.dest %>',
+ dest: 'test/browser/less.js'
+ },
+ dist: {
+ src: '<%= browserify.browser.dest %>',
+ dest: 'dist/less.js'
+ },
+ // Rhino
+ rhino: {
+ options: {
+ banner: '/* Less.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n',
+ footer: '' // override task-level footer
+ },
+ src: ['<%= build.rhino %>'],
+ dest: 'dist/less-rhino.js'
+ },
+ // lessc for Rhino
+ rhinolessc: {
+ options: {
+ banner: '/* Less.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n',
+ footer: '' // override task-level footer
+ },
+ src: ['<%= build.rhinolessc %>'],
+ dest: 'dist/lessc-rhino.js'
+ }
+ },
+
+ uglify: {
+ options: {
+ banner: '<%= meta.banner %>',
+ mangle: true,
+ compress: {
+ pure_getters: true
+ }
+ },
+ dist: {
+ src: ['<%= concat.dist.dest %>'],
+ dest: 'dist/less.min.js'
+ },
+ test: {
+ src: '<%= browserify.browser.dest %>',
+ dest: 'tmp/less.min.js'
+ }
+ },
+
+ jshint: {
+ options: {jshintrc: '.jshintrc'},
+ files: {
+ src: [
+ 'Gruntfile.js',
+ 'lib/less/**/*.js',
+ 'lib/less-node/**/*.js',
+ 'lib/less-browser/**/*.js',
+ 'lib/less-rhino/**/*.js',
+ 'bin/lessc'
+ ]
+ }
+ },
+
+ jscs: {
+ src: ["test/**/*.js", "lib/less*/**/*.js", "bin/lessc"],
+ options: {
+ config: ".jscsrc"
+ }
+ },
+
+ connect: {
+ server: {
+ options: {
+ port: 8081
+ }
+ }
+ },
+
+ jasmine: {
+ options: {
+ keepRunner: true,
+ host: 'http://localhost:8081/',
+ vendor: ['test/browser/jasmine-jsreporter.js', 'test/browser/common.js', 'test/browser/less.js'],
+ template: 'test/browser/test-runner-template.tmpl'
+ },
+ main: {
+ // src is used to build list of less files to compile
+ src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less', '!test/less/empty.less'],
+ options: {
+ helpers: 'test/browser/runner-main-options.js',
+ specs: 'test/browser/runner-main-spec.js',
+ outfile: 'tmp/browser/test-runner-main.html'
+ }
+ },
+ legacy: {
+ src: ['test/less/legacy/*.less'],
+ options: {
+ helpers: 'test/browser/runner-legacy-options.js',
+ specs: 'test/browser/runner-legacy-spec.js',
+ outfile: 'tmp/browser/test-runner-legacy.html'
+ }
+ },
+ strictUnits: {
+ src: ['test/less/strict-units/*.less'],
+ options: {
+ helpers: 'test/browser/runner-strict-units-options.js',
+ specs: 'test/browser/runner-strict-units-spec.js',
+ outfile: 'tmp/browser/test-runner-strict-units.html'
+ }
+ },
+ errors: {
+ src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'],
+ options: {
+ timeout: 20000,
+ helpers: 'test/browser/runner-errors-options.js',
+ specs: 'test/browser/runner-errors-spec.js',
+ outfile: 'tmp/browser/test-runner-errors.html'
+ }
+ },
+ noJsErrors: {
+ src: ['test/less/no-js-errors/*.less'],
+ options: {
+ helpers: 'test/browser/runner-no-js-errors-options.js',
+ specs: 'test/browser/runner-no-js-errors-spec.js',
+ outfile: 'tmp/browser/test-runner-no-js-errors.html'
+ }
+ },
+ browser: {
+ src: ['test/browser/less/*.less'],
+ options: {
+ helpers: 'test/browser/runner-browser-options.js',
+ specs: 'test/browser/runner-browser-spec.js',
+ outfile: 'tmp/browser/test-runner-browser.html'
+ }
+ },
+ relativeUrls: {
+ src: ['test/browser/less/relative-urls/*.less'],
+ options: {
+ helpers: 'test/browser/runner-relative-urls-options.js',
+ specs: 'test/browser/runner-relative-urls-spec.js',
+ outfile: 'tmp/browser/test-runner-relative-urls.html'
+ }
+ },
+ rootpath: {
+ src: ['test/browser/less/rootpath/*.less'],
+ options: {
+ helpers: 'test/browser/runner-rootpath-options.js',
+ specs: 'test/browser/runner-rootpath-spec.js',
+ outfile: 'tmp/browser/test-runner-rootpath.html'
+ }
+ },
+ rootpathRelative: {
+ src: ['test/browser/less/rootpath-relative/*.less'],
+ options: {
+ helpers: 'test/browser/runner-rootpath-relative-options.js',
+ specs: 'test/browser/runner-rootpath-relative-spec.js',
+ outfile: 'tmp/browser/test-runner-rootpath-relative.html'
+ }
+ },
+ production: {
+ src: ['test/browser/less/production/*.less'],
+ options: {
+ helpers: 'test/browser/runner-production-options.js',
+ specs: 'test/browser/runner-production-spec.js',
+ outfile: 'tmp/browser/test-runner-production.html'
+ }
+ },
+ modifyVars: {
+ src: ['test/browser/less/modify-vars/*.less'],
+ options: {
+ helpers: 'test/browser/runner-modify-vars-options.js',
+ specs: 'test/browser/runner-modify-vars-spec.js',
+ outfile: 'tmp/browser/test-runner-modify-vars.html'
+ }
+ },
+ globalVars: {
+ src: ['test/browser/less/global-vars/*.less'],
+ options: {
+ helpers: 'test/browser/runner-global-vars-options.js',
+ specs: 'test/browser/runner-global-vars-spec.js',
+ outfile: 'tmp/browser/test-runner-global-vars.html'
+ }
+ },
+ postProcessor: {
+ src: ['test/browser/less/postProcessor/*.less'],
+ options: {
+ helpers: 'test/browser/runner-postProcessor-options.js',
+ specs: 'test/browser/runner-postProcessor.js',
+ outfile: 'tmp/browser/test-runner-post-processor.html'
+ }
+ }
+ },
+
+ 'saucelabs-jasmine': {
+ all: {
+ options: {
+ urls: ["post-processor", "global-vars", "modify-vars", "production", "rootpath-relative",
+ "rootpath", "relative-urls", "browser", "no-js-errors", "legacy", "strict-units"
+ ].map(function(testName) {
+ return "http://localhost:8081/tmp/browser/test-runner-" + testName + ".html";
+ }),
+ testname: 'Sauce Unit Test for less.js',
+ browsers: [{
+ browserName: "chrome",
+ version: '',
+ platform: 'Windows 8'
+ },
+ {
+ browserName: "firefox",
+ version: '33',
+ platform: 'Linux'
+ },
+ {
+ browserName: "iPad",
+ version: '8.0',
+ platform: 'OS X 10.9',
+ 'device-orientation': 'portrait'
+ },
+ {
+ browserName: "internet explorer",
+ version: '8',
+ platform: 'Windows XP'
+ },
+ {
+ browserName: "internet explorer",
+ version: '9',
+ platform: 'Windows 7'
+ },
+ {
+ browserName: "internet explorer",
+ version: '10',
+ platform: 'Windows 7'
+ },
+ {
+ browserName: "internet explorer",
+ version: '11',
+ platform: 'Windows 8.1'
+ }],
+ sauceConfig: {
+ 'record-video': process.env.TRAVIS_BRANCH !== "master",
+ 'record-screenshots': process.env.TRAVIS_BRANCH !== "master",
+ 'idle-timeout': 100, 'max-duration': 120,
+ build: process.env.TRAVIS_BRANCH === "master" ? process.env.TRAVIS_JOB_ID : undefined,
+ tags: [process.env.TRAVIS_BUILD_NUMBER, process.env.TRAVIS_PULL_REQUEST, process.env.TRAVIS_BRANCH]
+ },
+ throttled: 3
+ }
+ }
+ },
+
+ // Clean the version of less built for the tests
+ clean: {
+ test: ['test/browser/less.js', 'tmp', 'test/less-bom'],
+ "sourcemap-test": ['test/sourcemaps/*.css', 'test/sourcemaps/*.map'],
+ sauce_log: ["sc_*.log"]
+ }
+ });
+
+ // Load these plugins to provide the necessary tasks
+ require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
+
+ // Actually load this plugin's task(s).
+ grunt.loadTasks('build/tasks');
+
+ // by default, run tests
+ grunt.registerTask('default', [
+ 'test'
+ ]);
+
+ grunt.registerTask('updateBowerJson', function () {
+ var bowerJson = require('./bower.json');
+ bowerJson.version = grunt.config('pkg.version');
+ fs.writeFileSync('./bower.json', JSON.stringify(bowerJson, null, 2));
+ });
+
+ // Release
+ grunt.registerTask('dist', [
+ 'browserify:browser',
+ 'concat:dist',
+ 'uglify:dist',
+ 'updateBowerJson'
+ ]);
+
+ // Release Rhino Version
+ grunt.registerTask('rhino', [
+ 'browserify:rhino',
+ 'concat:rhino',
+ 'concat:rhinolessc'
+ ]);
+
+ // Create the browser version of less.js
+ grunt.registerTask('browsertest-lessjs', [
+ 'browserify:browser',
+ 'uglify:test',
+ 'concat:browsertest'
+ ]);
+
+ // Run all browser tests
+ grunt.registerTask('browsertest', [
+ 'browsertest-lessjs',
+ 'connect',
+ 'jasmine'
+ ]);
+
+ // setup a web server to run the browser tests in a browser rather than phantom
+ grunt.registerTask('browsertest-server', [
+ 'browsertest-lessjs',
+ 'jasmine::build',
+ 'connect::keepalive'
+ ]);
+
+ var previous_force_state = grunt.option("force");
+
+ grunt.registerTask("force",function(set) {
+ if (set === "on") {
+ grunt.option("force",true);
+ }
+ else if (set === "off") {
+ grunt.option("force",false);
+ }
+ else if (set === "restore") {
+ grunt.option("force",previous_force_state);
+ }
+ });
+
+ grunt.registerTask('sauce', [
+ 'browsertest-lessjs',
+ 'jasmine::build',
+ 'connect',
+ 'sauce-after-setup'
+ ]);
+
+ // setup a web server to run the browser tests in a browser rather than phantom
+ grunt.registerTask('sauce-after-setup', [
+ 'saucelabs-jasmine',
+ 'clean:sauce_log'
+ ]);
+
+ var testTasks = [
+ 'clean',
+ 'jshint',
+ 'jscs',
+ 'shell:test',
+ 'browsertest'
+ ];
+
+ if (isNaN(Number(process.env.TRAVIS_PULL_REQUEST, 10)) &&
+ Number(process.env.TRAVIS_NODE_VERSION) === 0.11 &&
+ (process.env.TRAVIS_BRANCH === "master" || process.env.TRAVIS_BRANCH === "sauce")) {
+ testTasks.push("force:on");
+ testTasks.push("sauce-after-setup");
+ testTasks.push("force:off");
+ }
+
+ // Run all tests
+ grunt.registerTask('test', testTasks);
+
+ // generate a good test environment for testing sourcemaps
+ grunt.registerTask('sourcemap-test', [
+ 'clean:sourcemap-test',
+ 'shell:sourcemap-test',
+ 'connect::keepalive'
+ ]);
+
+ // Run benchmark
+ grunt.registerTask('benchmark', [
+ 'shell:benchmark'
+ ]);
+
+};
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
--- /dev/null
+[](http://badge.fury.io/js/less) [](https://travis-ci.org/less/less.js)
+[](https://david-dm.org/less/less.js) [](https://david-dm.org/less/less.js#info=devDependencies) [](https://david-dm.org/less/less.js#info=optionalDependencies)
+[](https://saucelabs.com/u/less) [](https://ci.appveyor.com/project/lukeapage/less-js/branch/master)
+
+# [Less.js](http://lesscss.org)
+
+> The **dynamic** stylesheet language. [http://lesscss.org](http://lesscss.org).
+
+This is the JavaScript, official, stable version of Less.
+
+
+## Getting Started
+
+Options for adding Less.js to your project:
+
+* Install with [npm](https://npmjs.org): `npm install less`
+* [Download the latest release][download]
+* Clone the repo: `git clone https://github.com/less/less.js.git`
+
+## More information
+
+For general information on the language, configuration options or usage visit [lesscss.org](http://lesscss.org).
+
+Here are other resources for using Less.js:
+
+* [stackoverflow.com][so] is a great place to get answers about Less.
+* [Less.js Issues][issues] for reporting bugs
+
+
+## Contributing
+Please read [CONTRIBUTING.md](CONTRIBUTING.md). Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com).
+
+### Reporting Issues
+
+Before opening any issue, please search for existing issues and read the [Issue Guidelines](https://github.com/necolas/issue-guidelines), written by [Nicolas Gallagher](https://github.com/necolas). After that if you find a bug or would like to make feature request, [please open a new issue][issues].
+
+Please report documentation issues in [the documentation project](https://github.com/less/less-docs).
+
+### Development
+
+Read [Developing Less](http://lesscss.org/usage/#developing-less).
+
+## Release History
+See the [changelog](CHANGELOG.md)
+
+## [License](LICENSE)
+
+Copyright (c) 2009-2014 [Alexis Sellier](http://cloudhead.io) & The Core Less Team
+Licensed under the [Apache License](LICENSE).
+
+
+[so]: http://stackoverflow.com/questions/tagged/twitter-bootstrap+less "StackOverflow.com"
+[issues]: https://github.com/less/less.js/issues "GitHub Issues for Less.js"
+[download]: https://github.com/less/less.js/zipball/master "Download Less.js"
--- /dev/null
+# Test against these versions of Node.js.
+environment:
+ matrix:
+ - nodejs_version: "0.10"
+ - nodejs_version: "0.11"
+ - nodejs_version: "0.12"
+
+# Allow failing jobs for bleeding-edge Node.js versions.
+matrix:
+ allow_failures:
+ - nodejs_version: "0.11"
+
+# Install scripts. (runs after repo cloning)
+install:
+ # Get the latest stable version of Node 0.STABLE.latest
+ - ps: Install-Product node $env:nodejs_version
+ # Use npm v2
+ - npm -g install npm@2
+ - set PATH=%APPDATA%\npm;%PATH%
+ - npm -v
+ # Typical npm stuff.
+ - npm install
+ # Grunt-specific stuff.
+ - npm install -g grunt-cli
+
+# Post-install test scripts.
+test_script:
+ # Output useful info for debugging.
+ - node --version
+ - npm --version
+ # Run test
+ - grunt test
+
+# Don't actually build.
+build: off
+
+# Set build version format here instead of in the admin panel.
+version: "{build}"
--- /dev/null
+#!/usr/bin/env node
+
+var path = require('path'),
+ fs = require('../lib/less-node/fs'),
+ os = require('os'),
+ errno,
+ mkdirp;
+
+try {
+ errno = require('errno');
+} catch (err) {
+ errno = null;
+}
+
+var less = require('../lib/less-node'),
+ pluginLoader = new less.PluginLoader(less),
+ plugin,
+ plugins = [];
+
+var args = process.argv.slice(1);
+var silent = false,
+ verbose = false,
+ options = {
+ depends: false,
+ compress: false,
+ max_line_len: -1,
+ lint: false,
+ paths: [],
+ color: true,
+ strictImports: false,
+ insecure: false,
+ rootpath: '',
+ relativeUrls: false,
+ ieCompat: true,
+ strictMath: false,
+ strictUnits: false,
+ globalVars: null,
+ modifyVars: null,
+ urlArgs: '',
+ plugins: plugins
+};
+var sourceMapOptions = {};
+var continueProcessing = true,
+ currentErrorcode;
+
+// calling process.exit does not flush stdout always
+// so use this to set the exit code
+process.on('exit', function() { process.reallyExit(currentErrorcode); });
+
+var checkArgFunc = function(arg, option) {
+ if (!option) {
+ console.log(arg + " option requires a parameter");
+ continueProcessing = false;
+ return false;
+ }
+ return true;
+};
+
+var checkBooleanArg = function(arg) {
+ var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg);
+ if (!onOff) {
+ console.log(" unable to parse " + arg + " as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
+ continueProcessing = false;
+ return false;
+ }
+ return Boolean(onOff[2]);
+};
+
+var parseVariableOption = function(option, variables) {
+ var parts = option.split('=', 2);
+ variables[parts[0]] = parts[1];
+};
+
+var warningMessages = "";
+var sourceMapFileInline = false;
+
+function printUsage() {
+ less.lesscHelper.printUsage();
+ pluginLoader.printUsage(plugins);
+ continueProcessing = false;
+}
+
+// self executing function so we can return
+(function() {
+ args = args.filter(function (arg) {
+ var match;
+
+ match = arg.match(/^-I(.+)$/);
+ if (match) {
+ options.paths.push(match[1]);
+ return false;
+ }
+
+ match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
+ if (match) {
+ arg = match[1];
+ } else {
+ return arg;
+ }
+
+ switch (arg) {
+ case 'v':
+ case 'version':
+ console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
+ continueProcessing = false;
+ break;
+ case 'verbose':
+ verbose = true;
+ break;
+ case 's':
+ case 'silent':
+ silent = true;
+ break;
+ case 'l':
+ case 'lint':
+ options.lint = true;
+ break;
+ case 'strict-imports':
+ options.strictImports = true;
+ break;
+ case 'h':
+ case 'help':
+ printUsage();
+ break;
+ case 'x':
+ case 'compress':
+ options.compress = true;
+ break;
+ case 'insecure':
+ options.insecure = true;
+ break;
+ case 'M':
+ case 'depends':
+ options.depends = true;
+ break;
+ case 'max-line-len':
+ if (checkArgFunc(arg, match[2])) {
+ options.maxLineLen = parseInt(match[2], 10);
+ if (options.maxLineLen <= 0) {
+ options.maxLineLen = -1;
+ }
+ }
+ break;
+ case 'no-color':
+ options.color = false;
+ break;
+ case 'no-ie-compat':
+ options.ieCompat = false;
+ break;
+ case 'no-js':
+ options.javascriptEnabled = false;
+ break;
+ case 'include-path':
+ if (checkArgFunc(arg, match[2])) {
+ options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':')
+ .map(function(p) {
+ if (p) {
+ return path.resolve(process.cwd(), p);
+ }
+ });
+ }
+ break;
+ case 'line-numbers':
+ if (checkArgFunc(arg, match[2])) {
+ options.dumpLineNumbers = match[2];
+ }
+ break;
+ case 'source-map':
+ options.sourceMap = true;
+ if (match[2]) {
+ sourceMapOptions.sourceMapFullFilename = match[2];
+ }
+ break;
+ case 'source-map-rootpath':
+ if (checkArgFunc(arg, match[2])) {
+ sourceMapOptions.sourceMapRootpath = match[2];
+ }
+ break;
+ case 'source-map-basepath':
+ if (checkArgFunc(arg, match[2])) {
+ sourceMapOptions.sourceMapBasepath = match[2];
+ }
+ break;
+ case 'source-map-map-inline':
+ sourceMapFileInline = true;
+ options.sourceMap = true;
+ break;
+ case 'source-map-less-inline':
+ sourceMapOptions.outputSourceFiles = true;
+ break;
+ case 'source-map-url':
+ if (checkArgFunc(arg, match[2])) {
+ sourceMapOptions.sourceMapURL = match[2];
+ }
+ break;
+ case 'rp':
+ case 'rootpath':
+ if (checkArgFunc(arg, match[2])) {
+ options.rootpath = match[2].replace(/\\/g, '/');
+ }
+ break;
+ case "ru":
+ case "relative-urls":
+ options.relativeUrls = true;
+ break;
+ case "sm":
+ case "strict-math":
+ if (checkArgFunc(arg, match[2])) {
+ options.strictMath = checkBooleanArg(match[2]);
+ }
+ break;
+ case "su":
+ case "strict-units":
+ if (checkArgFunc(arg, match[2])) {
+ options.strictUnits = checkBooleanArg(match[2]);
+ }
+ break;
+ case "global-var":
+ if (checkArgFunc(arg, match[2])) {
+ if (!options.globalVars) {
+ options.globalVars = {};
+ }
+ parseVariableOption(match[2], options.globalVars);
+ }
+ break;
+ case "modify-var":
+ if (checkArgFunc(arg, match[2])) {
+ if (!options.modifyVars) {
+ options.modifyVars = {};
+ }
+
+ parseVariableOption(match[2], options.modifyVars);
+ }
+ break;
+ case 'url-args':
+ if (checkArgFunc(arg, match[2])) {
+ options.urlArgs = match[2];
+ }
+ break;
+ case 'plugin':
+ var splitupArg = match[2].match(/^([^=]+)(=(.*))?/),
+ name = splitupArg[1],
+ pluginOptions = splitupArg[3];
+
+ plugin = pluginLoader.tryLoadPlugin(name, pluginOptions);
+ if (plugin) {
+ plugins.push(plugin);
+ } else {
+ console.log("Unable to load plugin " + name +
+ " please make sure that it is installed under or at the same level as less");
+ console.log();
+ printUsage();
+ currentErrorcode = 1;
+ }
+ break;
+ default:
+ plugin = pluginLoader.tryLoadPlugin("less-plugin-" + arg, match[2]);
+ if (plugin) {
+ plugins.push(plugin);
+ } else {
+ console.log("Unable to interpret argument " + arg +
+ " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under or at" +
+ " the same level as less");
+ console.log();
+ printUsage();
+ currentErrorcode = 1;
+ }
+ break;
+ }
+ });
+
+ if (!continueProcessing) {
+ return;
+ }
+
+ var input = args[1];
+ if (input && input != '-') {
+ input = path.resolve(process.cwd(), input);
+ }
+ var output = args[2];
+ var outputbase = args[2];
+ if (output) {
+ output = path.resolve(process.cwd(), output);
+ if (warningMessages) {
+ console.log(warningMessages);
+ }
+ }
+
+ if (options.sourceMap) {
+
+ sourceMapOptions.sourceMapInputFilename = input;
+ if (!sourceMapOptions.sourceMapFullFilename) {
+ if (!output && !sourceMapFileInline) {
+ console.log("the sourcemap option only has an optional filename if the css filename is given");
+ console.log("consider adding --source-map-map-inline which embeds the sourcemap into the css");
+ return;
+ }
+ // its in the same directory, so always just the basename
+ sourceMapOptions.sourceMapOutputFilename = path.basename(output);
+ sourceMapOptions.sourceMapFullFilename = output + ".map";
+ // its in the same directory, so always just the basename
+ sourceMapOptions.sourceMapFilename = path.basename(sourceMapOptions.sourceMapFullFilename);
+ } else if (options.sourceMap && !sourceMapFileInline) {
+ var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFullFilename),
+ mapDir = path.dirname(mapFilename),
+ outputDir = path.dirname(output);
+ // find the path from the map to the output file
+ sourceMapOptions.sourceMapOutputFilename = path.join(
+ path.relative(mapDir, outputDir), path.basename(output));
+
+ // make the sourcemap filename point to the sourcemap relative to the css file output directory
+ sourceMapOptions.sourceMapFilename = path.join(
+ path.relative(outputDir, mapDir), path.basename(sourceMapOptions.sourceMapFullFilename));
+ }
+ }
+
+ if (sourceMapOptions.sourceMapBasepath === undefined) {
+ sourceMapOptions.sourceMapBasepath = input ? path.dirname(input) : process.cwd();
+ }
+
+ if (sourceMapOptions.sourceMapRootpath === undefined) {
+ var pathToMap = path.dirname(sourceMapFileInline ? output : sourceMapOptions.sourceMapFullFilename),
+ pathToInput = path.dirname(sourceMapOptions.sourceMapInputFilename);
+ sourceMapOptions.sourceMapRootpath = path.relative(pathToMap, pathToInput);
+ }
+
+ if (! input) {
+ console.log("lessc: no input files");
+ console.log("");
+ printUsage();
+ currentErrorcode = 1;
+ return;
+ }
+
+ var ensureDirectory = function (filepath) {
+ var dir = path.dirname(filepath),
+ cmd,
+ existsSync = fs.existsSync || path.existsSync;
+ if (!existsSync(dir)) {
+ if (mkdirp === undefined) {
+ try {mkdirp = require('mkdirp');}
+ catch(e) { mkdirp = null; }
+ }
+ cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
+ cmd(dir);
+ }
+ };
+
+ if (options.depends) {
+ if (!outputbase) {
+ console.log("option --depends requires an output path to be specified");
+ return;
+ }
+ process.stdout.write(outputbase + ": ");
+ }
+
+ if (!sourceMapFileInline) {
+ var writeSourceMap = function(output, onDone) {
+ var filename = sourceMapOptions.sourceMapFullFilename;
+ ensureDirectory(filename);
+ fs.writeFile(filename, output, 'utf8', function (err) {
+ if (err) {
+ var description = "Error: ";
+ if (errno && errno.errno[err.errno]) {
+ description += errno.errno[err.errno].description;
+ } else {
+ description += err.code + " " + err.message;
+ }
+ less.logger.error('lessc: failed to create file ' + filename);
+ less.logger.error(description);
+ } else {
+ less.logger.info('lessc: wrote ' + filename);
+ }
+ onDone();
+ });
+ };
+ }
+
+ var writeSourceMapIfNeeded = function(output, onDone) {
+ if (options.sourceMap && !sourceMapFileInline) {
+ writeSourceMap(output, onDone);
+ } else {
+ onDone();
+ }
+ };
+
+ var writeOutput = function(output, result, onSuccess) {
+ if (output) {
+ ensureDirectory(output);
+ fs.writeFile(output, result.css, {encoding: 'utf8'}, function (err) {
+ if (err) {
+ var description = "Error: ";
+ if (errno && errno.errno[err.errno]) {
+ description += errno.errno[err.errno].description;
+ } else {
+ description += err.code + " " + err.message;
+ }
+ less.logger.error('lessc: failed to create file ' + output);
+ less.logger.error(description);
+ } else {
+ less.logger.info('lessc: wrote ' + output);
+ onSuccess();
+ }
+ });
+ } else if (!options.depends) {
+ process.stdout.write(result.css);
+ onSuccess();
+ }
+ };
+
+ var logDependencies = function(options, result) {
+ if (options.depends) {
+ var depends = "";
+ for (var i = 0; i < result.imports.length; i++) {
+ depends += result.imports[i] + " ";
+ }
+ console.log(depends);
+ }
+ };
+
+ var parseLessFile = function (e, data) {
+ if (e) {
+ console.log("lessc: " + e.message);
+ currentErrorcode = 1;
+ return;
+ }
+
+ data = data.replace(/^\uFEFF/, '');
+
+ options.paths = [path.dirname(input)].concat(options.paths);
+ options.filename = input;
+
+ if (options.lint) {
+ options.sourceMap = false;
+ }
+ sourceMapOptions.sourceMapFileInline = sourceMapFileInline;
+
+ if (options.sourceMap) {
+ options.sourceMap = sourceMapOptions;
+ }
+
+ less.logger.addListener({
+ info: function(msg) {
+ if (verbose) {
+ console.log(msg);
+ }
+ },
+ warn: function(msg) {
+ // do not show warning if outputting css to the console or the silent option is used
+ if (!silent && output) {
+ console.warn(msg);
+ }
+ },
+ error: function(msg) {
+ console.log(msg);
+ }
+ });
+
+ less.render(data, options)
+ .then(function(result) {
+ if (!options.lint) {
+ writeOutput(output, result, function() {
+ writeSourceMapIfNeeded(result.map, function() {
+ logDependencies(options, result);
+ });
+ });
+ }
+ },
+ function(err) {
+ less.writeError(err, options);
+ currentErrorcode = 1;
+ });
+ };
+
+ if (input != '-') {
+ fs.readFile(input, 'utf8', parseLessFile);
+ } else {
+ process.stdin.resume();
+ process.stdin.setEncoding('utf8');
+
+ var buffer = '';
+ process.stdin.on('data', function(data) {
+ buffer += data;
+ });
+
+ process.stdin.on('end', function() {
+ parseLessFile(false, buffer);
+ });
+ }
+})();
\ No newline at end of file
--- /dev/null
+{
+ "name": "less",
+ "version": "2.4.0",
+ "main": "dist/less.js",
+ "ignore": [
+ "**/.*",
+ "benchmark",
+ "bin",
+ "build",
+ "gradle",
+ "lib",
+ "test",
+ "*.md",
+ "LICENSE",
+ "Gruntfile.js",
+ "*.json",
+ "*.yml",
+ "build.gradle",
+ "gradlew",
+ "gradlew.bat",
+ ".gitattributes",
+ ".jshintrc",
+ ".npmignore"
+ ]
+}
\ No newline at end of file
--- /dev/null
+module.exports = require('./lib/less-browser');
--- /dev/null
+import groovy.io.FileType
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.gradle.api.tasks.Exec
+
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.eriwen:gradle-js-plugin:1.8.0'
+ classpath 'com.moowork.gradle:gradle-grunt-plugin:0.2'
+ }
+}
+
+apply plugin: 'js'
+apply plugin: 'grunt'
+
+repositories {
+ mavenCentral()
+}
+
+configurations {
+ rhino
+}
+
+dependencies {
+ rhino 'org.mozilla:rhino:1.7R4'
+}
+
+project.ext {
+ packageProps = new groovy.json.JsonSlurper().parseText(new File("package.json").toURL().text)
+ failures = 0;
+ rhinoTestSrc = "out/rhino-test-${packageProps.version}.js"
+ testSrc = 'test/less'
+ testOut = 'out/test'
+}
+
+task runGruntRhino(type: GruntTask) {
+ gruntArgs = "rhino"
+}
+
+combineJs {
+ dependsOn runGruntRhino
+ source = ["dist/less-rhino-${packageProps.version}.js", "test/rhino/test-header.js","dist/lessc-rhino-${packageProps.version}.js"]
+ dest = file(rhinoTestSrc)
+}
+
+task testRhino(type: AllRhinoTests) {
+// dependsOn 'testRhinoBase'
+ dependsOn 'testRhinoBase', 'testRhinoErrors', 'testRhinoLegacy', 'testRhinoStaticUrls', 'testRhinoCompression', 'testRhinoDebugAll', 'testRhinoDebugComments', 'testRhinoDebugMediaquery', 'testRhinoNoJsError', 'testRhinoSourceMap'
+}
+
+task testRhinoBase(type: RhinoTest) {
+ options = [ '--strict-math=true', '--relative-urls' ]
+}
+
+task testRhinoDebugAll(type: DebugRhinoTest) {
+ options = [ '--strict-math=true', '--line-numbers=all' ]
+ testDir = 'debug' + fs
+ suffix = "-all"
+}
+
+task testRhinoDebugComments(type: DebugRhinoTest) {
+ options = [ '--strict-math=true', '--line-numbers=comments' ]
+ testDir = 'debug' + fs
+ suffix = "-comments"
+}
+
+task testRhinoDebugMediaquery(type: DebugRhinoTest) {
+ options = [ '--strict-math=true', '--line-numbers=mediaquery' ]
+ testDir = 'debug' + fs
+ suffix = "-mediaquery"
+}
+
+task testRhinoErrors(type: RhinoTest) {
+ options = [ '--strict-math=true', '--strict-units=true' ]
+ testDir = 'errors/'
+ expectErrors = true
+}
+
+task testRhinoChyby(type: RhinoTest) {
+ options = [ '--strict-math=true', '--strict-units=true' ]
+ testDir = 'chyby/'
+ // expectErrors = true
+}
+
+task testRhinoNoJsError(type: RhinoTest) {
+ options = [ '--strict-math=true', '--strict-units=true', '--no-js' ]
+ testDir = 'no-js-errors/'
+ expectErrors = true
+}
+
+task testRhinoLegacy(type: RhinoTest) {
+ testDir = 'legacy/'
+}
+
+task testRhinoStaticUrls(type: RhinoTest) {
+ options = [ '--strict-math=true', '--rootpath=folder (1)/' ]
+ testDir = 'static-urls/'
+}
+
+task testRhinoCompression(type: RhinoTest) {
+ options = [ '--compress=true' ]
+ testDir = 'compression/'
+}
+
+task testRhinoSourceMap(type: SourceMapRhinoTest) {
+ options = [ '--strict-math=true', '--strict-units=true']
+ testDir = 'sourcemaps/'
+}
+
+task setupTest {
+ dependsOn combineJs
+ doLast {
+ file(testOut).deleteDir()
+ }
+}
+
+task clean << {
+ file(rhinoTestSrc).delete()
+ file(testOut).deleteDir()
+}
+
+class SourceMapRhinoTest extends RhinoTest {
+
+ // helper to get the output map file
+ def getOutputMap(lessFile) {
+ def outFile = project.file(lessFile.path.replace('test/less', project.testOut).replace('.less', '.css'))
+ return project.file(outFile.path + ".map");
+ }
+
+ // callback to add SourceMap options to the options list
+ def postProcessOptions(options, lessFile) {
+ def outFile = getOutputMap(lessFile)
+ project.file(outFile.parent).mkdirs()
+ options << "--source-map=${testDir}${lessFile.name.replace('.less','.css')}"
+ options << "--source-map-basepath=${lessRootDir}"
+ options << "--source-map-rootpath=testweb/"
+ options << "--source-map-output-map-file=${outFile}"
+
+ options
+ }
+
+ // Callback to validate output
+ def handleResult(exec, out, lessFile) {
+ def actualFile = getOutputMap(lessFile)
+ def expectedFile = project.file(projectDir + fs + "test" + fs + testDir + fs + lessFile.name.replace(".less", ".json"))
+ assert actualFile.text == expectedFile.text
+ }
+
+}
+
+class DebugRhinoTest extends RhinoTest {
+
+ def escapeIt(it) {
+ return it.replaceAll("\\\\", "\\\\\\\\").replaceAll("/", "\\\\/").replaceAll(":", "\\\\:").replaceAll("\\.", "\\\\.");
+ }
+
+ def globalReplacements(input, directory) {
+ def pDirectory = toPlatformFs(directory)
+ def p = lessRootDir + fs + pDirectory
+ def pathimport = p + toPlatformFs("import/")
+ def pathesc = escapeIt(p)
+ def pathimportesc = escapeIt(pathimport)
+
+ def result = input.replace("{path}", p).replace("{pathesc}", pathesc).replace("{pathimport}", pathimport)
+ return result.replace("{pathimportesc}", pathimportesc).replace("\r\n", "\n")
+ }
+}
+
+class RhinoTest extends DefaultTask {
+
+ RhinoTest() {
+ dependsOn 'setupTest'
+ }
+
+ def suffix = ""
+ def testDir = ''
+ def options = []
+ def expectErrors = false
+ def fs = File.separator;
+ def projectDir = toUpperCaseDriveLetter(System.getProperty("user.dir"));
+ def lessRootDir = projectDir + fs + "test" + fs + "less"
+
+ def toUpperCaseDriveLetter(path) {
+ if (path.charAt(1)==':' && path.charAt(2)=='\\') {
+ return path.substring(0,1).toUpperCase() + path.substring(1);
+ }
+ return path;
+ }
+
+ def toPlatformFs(path) {
+ return path.replace('\\', fs).replace('/', fs);
+ }
+
+ def expectedCssPath(lessFilePath) {
+ lessFilePath.replace('.less', "${suffix}.css").replace("${fs}less${fs}", "${fs}css${fs}");
+ }
+
+ def globalReplacements(input, directory) {
+ return input;
+ }
+
+ def stylize(str, style) {
+ def styles = [
+ reset : [0, 0],
+ bold : [1, 22],
+ inverse : [7, 27],
+ underline : [4, 24],
+ yellow : [33, 39],
+ green : [32, 39],
+ red : [31, 39],
+ grey : [90, 39]
+ ];
+ return '\033[' + styles[style][0] + 'm' + str +
+ '\033[' + styles[style][1] + 'm';
+ }
+
+ // Callback for subclasses to make any changes to the options
+ def postProcessOptions(options, lessFile) {
+ options
+ }
+
+ // Callback to validate output
+ def handleResult(exec, out, lessFile) {
+ def actual = out.toString().trim()
+ def actualResult = project.file(lessFile.path.replace('test/less', project.testOut).replace('.less', '.css'))
+ project.file(actualResult.parent).mkdirs()
+ actualResult << actual
+ def expected
+ if (expectErrors) {
+ assert exec.exitValue != 0
+ expected = project.file(lessFile.path.replace('.less', '.txt')).text.trim().
+ replace('{path}', lessFile.parent + '/').
+ replace('{pathhref}', '').
+ replace('{404status}', '')
+ } else {
+ assert exec.exitValue == 0
+ def expectedFile = expectedCssPath(lessFile.path)
+ expected = project.file(expectedFile).text.trim()
+ expected = globalReplacements(expected, testDir)
+ }
+ actual=actual.trim()
+ actual = actual.replace('\r\n', '\n')
+ expected = expected.replace('\r\n', '\n')
+ actual = actual.replace("/","\\")
+ expected = expected.replace("/","\\")
+// println "* actual *"
+// println actual
+// new File("actual.txt").write(actual)
+// println "* expected *"
+// println expected
+// new File("expected.txt").write(expected)
+ assert actual == expected
+ actualResult.delete()
+
+ }
+
+ @TaskAction
+ def runTest() {
+ int testSuccesses = 0, testFailures = 0, testErrors = 0
+ project.file('test/less/' + testDir).eachFileMatch(FileType.FILES, ~/.*\.less/) { lessFile ->
+ println "lessfile: $lessFile"
+ if (!project.hasProperty('test') || lessFile.name.startsWith(project.test)) {
+ def out = new java.io.ByteArrayOutputStream()
+ def processedOptions = postProcessOptions([project.rhinoTestSrc, lessFile] + options, lessFile)
+ def execOptions = {
+ main = 'org.mozilla.javascript.tools.shell.Main'
+// main = 'org.mozilla.javascript.tools.debugger.Main'
+ classpath = project.configurations.rhino
+ args = processedOptions
+ standardOutput = out
+ ignoreExitValue = true
+ }
+ println "rhinoTestSrc: ${project.rhinoTestSrc}"
+ try {
+ def exec = project.javaexec(execOptions)
+ handleResult(exec, out, lessFile)
+ testSuccesses++
+ println stylize(' ok', 'green')
+ }
+ catch (ex) {
+ println ex
+ println()
+ testErrors++;
+ }
+ catch (AssertionError ae) {
+ println stylize(' failed', 'red')
+ println ae
+ testFailures++
+ }
+ } else {
+ println stylize(' skipped', 'yellow')
+ }
+ }
+ println stylize(testSuccesses + ' ok', 'green')
+ println stylize(testFailures + ' assertion failed', testFailures == 0 ? 'green' : 'red')
+ println stylize(testErrors + ' errors', testErrors == 0 ? 'green' : 'red')
+ if (testFailures != 0 || testErrors != 0) {
+ project.failures++;
+ }
+ }
+}
+
+class AllRhinoTests extends DefaultTask {
+
+ AllRhinoTests() {
+ }
+
+ @TaskAction
+ def runTest() {
+ println stylize(project.failures + ' test suites failed', project.failures == 0 ? 'green' : 'red')
+ }
+
+ def stylize(str, style) {
+ def styles = [
+ reset : [0, 0],
+ bold : [1, 22],
+ inverse : [7, 27],
+ underline : [4, 24],
+ yellow : [33, 39],
+ green : [32, 39],
+ red : [31, 39],
+ grey : [90, 39]
+ ];
+ return '\033[' + styles[style][0] + 'm' + str +
+ '\033[' + styles[style][1] + 'm';
+ }
+}
+
+class GruntTask extends Exec {
+ private String gruntExecutable = Os.isFamily(Os.FAMILY_WINDOWS) ? "grunt.cmd" : "grunt"
+ private String switches = "--no-color"
+
+ String gruntArgs = ""
+
+ public GruntTask() {
+ super()
+ this.setExecutable(gruntExecutable)
+ }
+
+ public void setGruntArgs(String gruntArgs) {
+ this.args = "$switches $gruntArgs".trim().split(" ") as List
+ }
+}
+
--- /dev/null
+/*!\r
+ * Less - Leaner CSS v2.4.0\r
+ * http://lesscss.org\r
+ *\r
+ * Copyright (c) 2009-2015, Alexis Sellier <self@cloudhead.net>\r
+ * Licensed under the Apache v2 License.\r
+ *\r
+ */\r
+\r
+ /** * @license Apache v2\r
+ */\r
+\r
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.less=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+var addDataAttr = require("./utils").addDataAttr,
+ browser = require("./browser");
+
+module.exports = function(window, options) {
+
+ // use options from the current script tag data attribues
+ addDataAttr(options, browser.currentScript(window));
+
+ if (options.isFileProtocol === undefined) {
+ options.isFileProtocol = /^(file|(chrome|safari)(-extension)?|resource|qrc|app):/.test(window.location.protocol);
+ }
+
+ // Load styles asynchronously (default: false)
+ //
+ // This is set to `false` by default, so that the body
+ // doesn't start loading before the stylesheets are parsed.
+ // Setting this to `true` can result in flickering.
+ //
+ options.async = options.async || false;
+ options.fileAsync = options.fileAsync || false;
+
+ // Interval between watch polls
+ options.poll = options.poll || (options.isFileProtocol ? 1000 : 1500);
+
+ options.env = options.env || (window.location.hostname == '127.0.0.1' ||
+ window.location.hostname == '0.0.0.0' ||
+ window.location.hostname == 'localhost' ||
+ (window.location.port &&
+ window.location.port.length > 0) ||
+ options.isFileProtocol ? 'development'
+ : 'production');
+
+ var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(window.location.hash);
+ if (dumpLineNumbers) {
+ options.dumpLineNumbers = dumpLineNumbers[1];
+ }
+
+ if (options.useFileCache === undefined) {
+ options.useFileCache = true;
+ }
+
+ if (options.onReady === undefined) {
+ options.onReady = true;
+ }
+
+};
+
+},{"./browser":3,"./utils":9}],2:[function(require,module,exports){
+/**
+ * Kicks off less and compiles any stylesheets
+ * used in the browser distributed version of less
+ * to kick-start less using the browser api
+ */
+/*global window */
+
+// shim Promise if required
+require('promise/polyfill.js');
+
+var options = window.less || {};
+require("./add-default-options")(window, options);
+
+var less = module.exports = require("./index")(window, options);
+
+if (options.onReady) {
+ if (/!watch/.test(window.location.hash)) {
+ less.watch();
+ }
+
+ less.pageLoadFinished = less.registerStylesheets().then(
+ function () {
+ return less.refresh(less.env === 'development');
+ }
+ );
+}
+},{"./add-default-options":1,"./index":7,"promise/polyfill.js":"promise/polyfill.js"}],3:[function(require,module,exports){
+var utils = require("./utils");
+module.exports = {
+ createCSS: function (document, styles, sheet) {
+ // Strip the query-string
+ var href = sheet.href || '';
+
+ // If there is no title set, use the filename, minus the extension
+ var id = 'less:' + (sheet.title || utils.extractId(href));
+
+ // If this has already been inserted into the DOM, we may need to replace it
+ var oldStyleNode = document.getElementById(id);
+ var keepOldStyleNode = false;
+
+ // Create a new stylesheet node for insertion or (if necessary) replacement
+ var styleNode = document.createElement('style');
+ styleNode.setAttribute('type', 'text/css');
+ if (sheet.media) {
+ styleNode.setAttribute('media', sheet.media);
+ }
+ styleNode.id = id;
+
+ if (!styleNode.styleSheet) {
+ styleNode.appendChild(document.createTextNode(styles));
+
+ // If new contents match contents of oldStyleNode, don't replace oldStyleNode
+ keepOldStyleNode = (oldStyleNode !== null && oldStyleNode.childNodes.length > 0 && styleNode.childNodes.length > 0 &&
+ oldStyleNode.firstChild.nodeValue === styleNode.firstChild.nodeValue);
+ }
+
+ var head = document.getElementsByTagName('head')[0];
+
+ // If there is no oldStyleNode, just append; otherwise, only append if we need
+ // to replace oldStyleNode with an updated stylesheet
+ if (oldStyleNode === null || keepOldStyleNode === false) {
+ var nextEl = sheet && sheet.nextSibling || null;
+ if (nextEl) {
+ nextEl.parentNode.insertBefore(styleNode, nextEl);
+ } else {
+ head.appendChild(styleNode);
+ }
+ }
+ if (oldStyleNode && keepOldStyleNode === false) {
+ oldStyleNode.parentNode.removeChild(oldStyleNode);
+ }
+
+ // For IE.
+ // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash.
+ // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head
+ if (styleNode.styleSheet) {
+ try {
+ styleNode.styleSheet.cssText = styles;
+ } catch (e) {
+ throw new Error("Couldn't reassign styleSheet.cssText.");
+ }
+ }
+ },
+ currentScript: function(window) {
+ var document = window.document;
+ return document.currentScript || (function() {
+ var scripts = document.getElementsByTagName("script");
+ return scripts[scripts.length - 1];
+ })();
+ }
+};
+
+},{"./utils":9}],4:[function(require,module,exports){
+// Cache system is a bit outdated and could do with work
+
+module.exports = function(window, options, logger) {
+ var cache = null;
+ if (options.env !== 'development') {
+ try {
+ cache = (typeof window.localStorage === 'undefined') ? null : window.localStorage;
+ } catch (_) {}
+ }
+ return {
+ setCSS: function(path, lastModified, styles) {
+ if (cache) {
+ logger.info('saving ' + path + ' to cache.');
+ try {
+ cache.setItem(path, styles);
+ cache.setItem(path + ':timestamp', lastModified);
+ } catch(e) {
+ //TODO - could do with adding more robust error handling
+ logger.error('failed to save "' + path + '" to local storage for caching.');
+ }
+ }
+ },
+ getCSS: function(path, webInfo) {
+ var css = cache && cache.getItem(path),
+ timestamp = cache && cache.getItem(path + ':timestamp');
+
+ if (timestamp && webInfo.lastModified &&
+ (new Date(webInfo.lastModified).valueOf() ===
+ new Date(timestamp).valueOf())) {
+ // Use local copy
+ return css;
+ }
+ }
+ };
+};
+
+},{}],5:[function(require,module,exports){
+var utils = require("./utils"),
+ browser = require("./browser");
+
+module.exports = function(window, less, options) {
+
+ function errorHTML(e, rootHref) {
+ var id = 'less-error-message:' + utils.extractId(rootHref || "");
+ var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
+ var elem = window.document.createElement('div'), timer, content, errors = [];
+ var filename = e.filename || rootHref;
+ var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1];
+
+ elem.id = id;
+ elem.className = "less-error-message";
+
+ content = '<h3>' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
+ '</h3>' + '<p>in <a href="' + filename + '">' + filenameNoPath + "</a> ";
+
+ var errorline = function (e, i, classname) {
+ if (e.extract[i] !== undefined) {
+ errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
+ .replace(/\{class\}/, classname)
+ .replace(/\{content\}/, e.extract[i]));
+ }
+ };
+
+ if (e.extract) {
+ errorline(e, 0, '');
+ errorline(e, 1, 'line');
+ errorline(e, 2, '');
+ content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
+ '<ul>' + errors.join('') + '</ul>';
+ }
+ if (e.stack && (e.extract || options.logLevel >= 4)) {
+ content += '<br/>Stack Trace</br />' + e.stack.split('\n').slice(1).join('<br/>');
+ }
+ elem.innerHTML = content;
+
+ // CSS for error messages
+ browser.createCSS(window.document, [
+ '.less-error-message ul, .less-error-message li {',
+ 'list-style-type: none;',
+ 'margin-right: 15px;',
+ 'padding: 4px 0;',
+ 'margin: 0;',
+ '}',
+ '.less-error-message label {',
+ 'font-size: 12px;',
+ 'margin-right: 15px;',
+ 'padding: 4px 0;',
+ 'color: #cc7777;',
+ '}',
+ '.less-error-message pre {',
+ 'color: #dd6666;',
+ 'padding: 4px 0;',
+ 'margin: 0;',
+ 'display: inline-block;',
+ '}',
+ '.less-error-message pre.line {',
+ 'color: #ff0000;',
+ '}',
+ '.less-error-message h3 {',
+ 'font-size: 20px;',
+ 'font-weight: bold;',
+ 'padding: 15px 0 5px 0;',
+ 'margin: 0;',
+ '}',
+ '.less-error-message a {',
+ 'color: #10a',
+ '}',
+ '.less-error-message .error {',
+ 'color: red;',
+ 'font-weight: bold;',
+ 'padding-bottom: 2px;',
+ 'border-bottom: 1px dashed red;',
+ '}'
+ ].join('\n'), { title: 'error-message' });
+
+ elem.style.cssText = [
+ "font-family: Arial, sans-serif",
+ "border: 1px solid #e00",
+ "background-color: #eee",
+ "border-radius: 5px",
+ "-webkit-border-radius: 5px",
+ "-moz-border-radius: 5px",
+ "color: #e00",
+ "padding: 15px",
+ "margin-bottom: 15px"
+ ].join(';');
+
+ if (options.env === 'development') {
+ timer = setInterval(function () {
+ var document = window.document,
+ body = document.body;
+ if (body) {
+ if (document.getElementById(id)) {
+ body.replaceChild(elem, document.getElementById(id));
+ } else {
+ body.insertBefore(elem, body.firstChild);
+ }
+ clearInterval(timer);
+ }
+ }, 10);
+ }
+ }
+
+ function error(e, rootHref) {
+ if (!options.errorReporting || options.errorReporting === "html") {
+ errorHTML(e, rootHref);
+ } else if (options.errorReporting === "console") {
+ errorConsole(e, rootHref);
+ } else if (typeof options.errorReporting === 'function') {
+ options.errorReporting("add", e, rootHref);
+ }
+ }
+
+ function removeErrorHTML(path) {
+ var node = window.document.getElementById('less-error-message:' + utils.extractId(path));
+ if (node) {
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ function removeErrorConsole(path) {
+ //no action
+ }
+
+ function removeError(path) {
+ if (!options.errorReporting || options.errorReporting === "html") {
+ removeErrorHTML(path);
+ } else if (options.errorReporting === "console") {
+ removeErrorConsole(path);
+ } else if (typeof options.errorReporting === 'function') {
+ options.errorReporting("remove", path);
+ }
+ }
+
+ function errorConsole(e, rootHref) {
+ var template = '{line} {content}';
+ var filename = e.filename || rootHref;
+ var errors = [];
+ var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
+ " in " + filename + " ";
+
+ var errorline = function (e, i, classname) {
+ if (e.extract[i] !== undefined) {
+ errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
+ .replace(/\{class\}/, classname)
+ .replace(/\{content\}/, e.extract[i]));
+ }
+ };
+
+ if (e.extract) {
+ errorline(e, 0, '');
+ errorline(e, 1, 'line');
+ errorline(e, 2, '');
+ content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' +
+ errors.join('\n');
+ }
+ if (e.stack && (e.extract || options.logLevel >= 4)) {
+ content += '\nStack Trace\n' + e.stack;
+ }
+ less.logger.error(content);
+ }
+
+ return {
+ add: error,
+ remove: removeError
+ };
+};
+
+},{"./browser":3,"./utils":9}],6:[function(require,module,exports){
+/*global window, XMLHttpRequest */
+
+module.exports = function(options, logger) {
+
+ var AbstractFileManager = require("../less/environment/abstract-file-manager.js");
+
+ var fileCache = {};
+
+ //TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
+
+ function getXMLHttpRequest() {
+ if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
+ return new XMLHttpRequest();
+ } else {
+ try {
+ /*global ActiveXObject */
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e) {
+ logger.error("browser doesn't support AJAX.");
+ return null;
+ }
+ }
+ }
+
+ var FileManager = function() {
+ };
+
+ FileManager.prototype = new AbstractFileManager();
+
+ FileManager.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() {
+ return true;
+ };
+ FileManager.prototype.join = function join(basePath, laterPath) {
+ if (!basePath) {
+ return laterPath;
+ }
+ return this.extractUrlParts(laterPath, basePath).path;
+ };
+ FileManager.prototype.doXHR = function doXHR(url, type, callback, errback) {
+
+ var xhr = getXMLHttpRequest();
+ var async = options.isFileProtocol ? options.fileAsync : options.async;
+
+ if (typeof xhr.overrideMimeType === 'function') {
+ xhr.overrideMimeType('text/css');
+ }
+ logger.debug("XHR: Getting '" + url + "'");
+ xhr.open('GET', url, async);
+ xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
+ xhr.send(null);
+
+ function handleResponse(xhr, callback, errback) {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ callback(xhr.responseText,
+ xhr.getResponseHeader("Last-Modified"));
+ } else if (typeof errback === 'function') {
+ errback(xhr.status, url);
+ }
+ }
+
+ if (options.isFileProtocol && !options.fileAsync) {
+ if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
+ callback(xhr.responseText);
+ } else {
+ errback(xhr.status, url);
+ }
+ } else if (async) {
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ handleResponse(xhr, callback, errback);
+ }
+ };
+ } else {
+ handleResponse(xhr, callback, errback);
+ }
+ };
+ FileManager.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return true;
+ };
+
+ FileManager.prototype.clearFileCache = function() {
+ fileCache = {};
+ };
+
+ FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment, callback) {
+ if (currentDirectory && !this.isPathAbsolute(filename)) {
+ filename = currentDirectory + filename;
+ }
+
+ options = options || {};
+
+ // sheet may be set to the stylesheet for the initial load or a collection of properties including
+ // some context variables for imports
+ var hrefParts = this.extractUrlParts(filename, window.location.href);
+ var href = hrefParts.url;
+
+ if (options.useFileCache && fileCache[href]) {
+ try {
+ var lessText = fileCache[href];
+ callback(null, { contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
+ } catch (e) {
+ callback({filename: href, message: "Error loading file " + href + " error was " + e.message});
+ }
+ return;
+ }
+
+ this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
+ // per file cache
+ fileCache[href] = data;
+
+ // Use remote copy (re-parse)
+ callback(null, { contents: data, filename: href, webInfo: { lastModified: lastModified }});
+ }, function doXHRError(status, url) {
+ callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href });
+ });
+ };
+
+ return FileManager;
+};
+
+},{"../less/environment/abstract-file-manager.js":14}],7:[function(require,module,exports){
+//
+// index.js
+// Should expose the additional browser functions on to the less object
+//
+var addDataAttr = require("./utils").addDataAttr,
+ browser = require("./browser");
+
+module.exports = function(window, options) {
+ var document = window.document;
+ var less = require('../less')();
+ //module.exports = less;
+ less.options = options;
+ var environment = less.environment,
+ FileManager = require("./file-manager")(options, less.logger),
+ fileManager = new FileManager();
+ environment.addFileManager(fileManager);
+ less.FileManager = FileManager;
+
+ require("./log-listener")(less, options);
+ var errors = require("./error-reporting")(window, less, options);
+ var cache = less.cache = options.cache || require("./cache")(window, options, less.logger);
+
+ //Setup user functions
+ if (options.functions) {
+ less.functions.functionRegistry.addMultiple(options.functions);
+ }
+
+ var typePattern = /^text\/(x-)?less$/;
+
+ function postProcessCSS(styles) {
+ if (options.postProcessor && typeof options.postProcessor === 'function') {
+ styles = options.postProcessor.call(styles, styles) || styles;
+ }
+ return styles;
+ }
+
+ function clone(obj) {
+ var cloned = {};
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ cloned[prop] = obj[prop];
+ }
+ }
+ return cloned;
+ }
+
+ // only really needed for phantom
+ function bind(func, thisArg) {
+ var curryArgs = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0));
+ return func.apply(thisArg, args);
+ };
+ }
+
+ function loadStyles(modifyVars) {
+ var styles = document.getElementsByTagName('style'),
+ style;
+
+ for (var i = 0; i < styles.length; i++) {
+ style = styles[i];
+ if (style.type.match(typePattern)) {
+ var instanceOptions = clone(options);
+ instanceOptions.modifyVars = modifyVars;
+ var lessText = style.innerHTML || '';
+ instanceOptions.filename = document.location.href.replace(/#.*$/, '');
+
+ /*jshint loopfunc:true */
+ // use closure to store current style
+ less.render(lessText, instanceOptions,
+ bind(function(style, e, result) {
+ if (e) {
+ errors.add(e, "inline");
+ } else {
+ style.type = 'text/css';
+ if (style.styleSheet) {
+ style.styleSheet.cssText = result.css;
+ } else {
+ style.innerHTML = result.css;
+ }
+ }
+ }, null, style));
+ }
+ }
+ }
+
+ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
+
+ var instanceOptions = clone(options);
+ addDataAttr(instanceOptions, sheet);
+ instanceOptions.mime = sheet.type;
+
+ if (modifyVars) {
+ instanceOptions.modifyVars = modifyVars;
+ }
+
+ function loadInitialFileCallback(loadedFile) {
+
+ var data = loadedFile.contents,
+ path = loadedFile.filename,
+ webInfo = loadedFile.webInfo;
+
+ var newFileInfo = {
+ currentDirectory: fileManager.getPath(path),
+ filename: path,
+ rootFilename: path,
+ relativeUrls: instanceOptions.relativeUrls};
+
+ newFileInfo.entryPath = newFileInfo.currentDirectory;
+ newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory;
+
+ if (webInfo) {
+ webInfo.remaining = remaining;
+
+ if (!instanceOptions.modifyVars) {
+ var css = cache.getCSS(path, webInfo);
+ if (!reload && css) {
+ webInfo.local = true;
+ callback(null, css, data, sheet, webInfo, path);
+ return;
+ }
+ }
+ }
+
+ //TODO add tests around how this behaves when reloading
+ errors.remove(path);
+
+ instanceOptions.rootFileInfo = newFileInfo;
+ less.render(data, instanceOptions, function(e, result) {
+ if (e) {
+ e.href = path;
+ callback(e);
+ } else {
+ result.css = postProcessCSS(result.css);
+ if (!instanceOptions.modifyVars) {
+ cache.setCSS(sheet.href, webInfo.lastModified, result.css);
+ }
+ callback(null, result.css, data, sheet, webInfo, path);
+ }
+ });
+ }
+
+ fileManager.loadFile(sheet.href, null, instanceOptions, environment, function(e, loadedFile) {
+ if (e) {
+ callback(e);
+ return;
+ }
+ loadInitialFileCallback(loadedFile);
+ });
+ }
+
+ function loadStyleSheets(callback, reload, modifyVars) {
+ for (var i = 0; i < less.sheets.length; i++) {
+ loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars);
+ }
+ }
+
+ function initRunningMode() {
+ if (less.env === 'development') {
+ less.watchTimer = setInterval(function () {
+ if (less.watchMode) {
+ fileManager.clearFileCache();
+ loadStyleSheets(function (e, css, _, sheet, webInfo) {
+ if (e) {
+ errors.add(e, e.href || sheet.href);
+ } else if (css) {
+ browser.createCSS(window.document, css, sheet);
+ }
+ });
+ }
+ }, options.poll);
+ }
+ }
+
+ //
+ // Watch mode
+ //
+ less.watch = function () {
+ if (!less.watchMode ) {
+ less.env = 'development';
+ initRunningMode();
+ }
+ this.watchMode = true;
+ return true;
+ };
+
+ less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; };
+
+ //
+ // Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
+ //
+ less.registerStylesheets = function() {
+ return new Promise(function(resolve, reject) {
+ var links = document.getElementsByTagName('link');
+ less.sheets = [];
+
+ for (var i = 0; i < links.length; i++) {
+ if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
+ (links[i].type.match(typePattern)))) {
+ less.sheets.push(links[i]);
+ }
+ }
+
+ resolve();
+ });
+ };
+
+ //
+ // With this function, it's possible to alter variables and re-render
+ // CSS without reloading less-files
+ //
+ less.modifyVars = function(record) {
+ return less.refresh(true, record, false);
+ };
+
+ less.refresh = function (reload, modifyVars, clearFileCache) {
+ if ((reload || clearFileCache) && clearFileCache !== false) {
+ fileManager.clearFileCache();
+ }
+ return new Promise(function (resolve, reject) {
+ var startTime, endTime, totalMilliseconds;
+ startTime = endTime = new Date();
+
+ loadStyleSheets(function (e, css, _, sheet, webInfo) {
+ if (e) {
+ errors.add(e, e.href || sheet.href);
+ reject(e);
+ return;
+ }
+ if (webInfo.local) {
+ less.logger.info("loading " + sheet.href + " from cache.");
+ } else {
+ less.logger.info("rendered " + sheet.href + " successfully.");
+ }
+ browser.createCSS(window.document, css, sheet);
+ less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms');
+ if (webInfo.remaining === 0) {
+ totalMilliseconds = new Date() - startTime;
+ less.logger.info("less has finished. css generated in " + totalMilliseconds + 'ms');
+ resolve({
+ startTime: startTime,
+ endTime: endTime,
+ totalMilliseconds: totalMilliseconds,
+ sheets: less.sheets.length
+ });
+ }
+ endTime = new Date();
+ }, reload, modifyVars);
+
+ loadStyles(modifyVars);
+ });
+ };
+
+ less.refreshStyles = loadStyles;
+ return less;
+};
+
+},{"../less":29,"./browser":3,"./cache":4,"./error-reporting":5,"./file-manager":6,"./log-listener":8,"./utils":9}],8:[function(require,module,exports){
+module.exports = function(less, options) {
+
+ var logLevel_debug = 4,
+ logLevel_info = 3,
+ logLevel_warn = 2,
+ logLevel_error = 1;
+
+ // The amount of logging in the javascript console.
+ // 3 - Debug, information and errors
+ // 2 - Information and errors
+ // 1 - Errors
+ // 0 - None
+ // Defaults to 2
+ options.logLevel = typeof options.logLevel !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_info : logLevel_error);
+
+ if (!options.loggers) {
+ options.loggers = [{
+ debug: function(msg) {
+ if (options.logLevel >= logLevel_debug) {
+ console.log(msg);
+ }
+ },
+ info: function(msg) {
+ if (options.logLevel >= logLevel_info) {
+ console.log(msg);
+ }
+ },
+ warn: function(msg) {
+ if (options.logLevel >= logLevel_warn) {
+ console.warn(msg);
+ }
+ },
+ error: function(msg) {
+ if (options.logLevel >= logLevel_error) {
+ console.error(msg);
+ }
+ }
+ }];
+ }
+ for (var i = 0; i < options.loggers.length; i++) {
+ less.logger.addListener(options.loggers[i]);
+ }
+};
+
+},{}],9:[function(require,module,exports){
+module.exports = {
+ extractId: function(href) {
+ return href.replace(/^[a-z-]+:\/+?[^\/]+/, '') // Remove protocol & domain
+ .replace(/[\?\&]livereload=\w+/, '') // Remove LiveReload cachebuster
+ .replace(/^\//, '') // Remove root /
+ .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension
+ .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
+ .replace(/\./g, ':'); // Replace dots with colons(for valid id)
+ },
+ addDataAttr: function(options, tag) {
+ for (var opt in tag.dataset) {
+ if (tag.dataset.hasOwnProperty(opt)) {
+ if (opt === "env" || opt === "dumpLineNumbers" || opt === "rootpath" || opt === "errorReporting") {
+ options[opt] = tag.dataset[opt];
+ } else {
+ try {
+ options[opt] = JSON.parse(tag.dataset[opt]);
+ }
+ catch(_) {}
+ }
+ }
+ }
+ }
+};
+
+},{}],10:[function(require,module,exports){
+var contexts = {};
+module.exports = contexts;
+
+var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
+ if (!original) { return; }
+
+ for (var i = 0; i < propertiesToCopy.length; i++) {
+ if (original.hasOwnProperty(propertiesToCopy[i])) {
+ destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
+ }
+ }
+};
+
+/*
+ parse is used whilst parsing
+ */
+var parseCopyProperties = [
+ // options
+ 'paths', // option - unmodified - paths to search for imports on
+ 'relativeUrls', // option - whether to adjust URL's to be relative
+ 'rootpath', // option - rootpath to append to URL's
+ 'strictImports', // option -
+ 'insecure', // option - whether to allow imports from insecure ssl hosts
+ 'dumpLineNumbers', // option - whether to dump line numbers
+ 'compress', // option - whether to compress
+ 'syncImport', // option - whether to import synchronously
+ 'chunkInput', // option - whether to chunk input. more performant but causes parse issues.
+ 'mime', // browser only - mime type for sheet import
+ 'useFileCache', // browser only - whether to use the per file session cache
+ // context
+ 'processImports', // option & context - whether to process imports. if false then imports will not be imported.
+ // Used by the import manager to stop multiple import visitors being created.
+ 'reference', // Used to indicate that the contents are imported by reference
+ 'pluginManager' // Used as the plugin manager for the session
+];
+
+contexts.Parse = function(options) {
+ copyFromOriginal(options, this, parseCopyProperties);
+
+ if (typeof this.paths === "string") { this.paths = [this.paths]; }
+};
+
+var evalCopyProperties = [
+ 'paths', // additional include paths
+ 'compress', // whether to compress
+ 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
+ 'strictMath', // whether math has to be within parenthesis
+ 'strictUnits', // whether units need to evaluate correctly
+ 'sourceMap', // whether to output a source map
+ 'importMultiple', // whether we are currently importing multiple copies
+ 'urlArgs', // whether to add args into url tokens
+ 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
+ 'pluginManager', // Used as the plugin manager for the session
+ 'importantScope' // used to bubble up !important statements
+ ];
+
+contexts.Eval = function(options, frames) {
+ copyFromOriginal(options, this, evalCopyProperties);
+
+ this.frames = frames || [];
+ this.importantScope = this.importantScope || [];
+};
+
+contexts.Eval.prototype.inParenthesis = function () {
+ if (!this.parensStack) {
+ this.parensStack = [];
+ }
+ this.parensStack.push(true);
+};
+
+contexts.Eval.prototype.outOfParenthesis = function () {
+ this.parensStack.pop();
+};
+
+contexts.Eval.prototype.isMathOn = function () {
+ return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
+};
+
+contexts.Eval.prototype.isPathRelative = function (path) {
+ return !/^(?:[a-z-]+:|\/|#)/i.test(path);
+};
+
+contexts.Eval.prototype.normalizePath = function( path ) {
+ var
+ segments = path.split("/").reverse(),
+ segment;
+
+ path = [];
+ while (segments.length !== 0 ) {
+ segment = segments.pop();
+ switch( segment ) {
+ case ".":
+ break;
+ case "..":
+ if ((path.length === 0) || (path[path.length - 1] === "..")) {
+ path.push( segment );
+ } else {
+ path.pop();
+ }
+ break;
+ default:
+ path.push( segment );
+ break;
+ }
+ }
+
+ return path.join("/");
+};
+
+//todo - do the same for the toCSS ?
+
+},{}],11:[function(require,module,exports){
+module.exports = {
+ 'aliceblue':'#f0f8ff',
+ 'antiquewhite':'#faebd7',
+ 'aqua':'#00ffff',
+ 'aquamarine':'#7fffd4',
+ 'azure':'#f0ffff',
+ 'beige':'#f5f5dc',
+ 'bisque':'#ffe4c4',
+ 'black':'#000000',
+ 'blanchedalmond':'#ffebcd',
+ 'blue':'#0000ff',
+ 'blueviolet':'#8a2be2',
+ 'brown':'#a52a2a',
+ 'burlywood':'#deb887',
+ 'cadetblue':'#5f9ea0',
+ 'chartreuse':'#7fff00',
+ 'chocolate':'#d2691e',
+ 'coral':'#ff7f50',
+ 'cornflowerblue':'#6495ed',
+ 'cornsilk':'#fff8dc',
+ 'crimson':'#dc143c',
+ 'cyan':'#00ffff',
+ 'darkblue':'#00008b',
+ 'darkcyan':'#008b8b',
+ 'darkgoldenrod':'#b8860b',
+ 'darkgray':'#a9a9a9',
+ 'darkgrey':'#a9a9a9',
+ 'darkgreen':'#006400',
+ 'darkkhaki':'#bdb76b',
+ 'darkmagenta':'#8b008b',
+ 'darkolivegreen':'#556b2f',
+ 'darkorange':'#ff8c00',
+ 'darkorchid':'#9932cc',
+ 'darkred':'#8b0000',
+ 'darksalmon':'#e9967a',
+ 'darkseagreen':'#8fbc8f',
+ 'darkslateblue':'#483d8b',
+ 'darkslategray':'#2f4f4f',
+ 'darkslategrey':'#2f4f4f',
+ 'darkturquoise':'#00ced1',
+ 'darkviolet':'#9400d3',
+ 'deeppink':'#ff1493',
+ 'deepskyblue':'#00bfff',
+ 'dimgray':'#696969',
+ 'dimgrey':'#696969',
+ 'dodgerblue':'#1e90ff',
+ 'firebrick':'#b22222',
+ 'floralwhite':'#fffaf0',
+ 'forestgreen':'#228b22',
+ 'fuchsia':'#ff00ff',
+ 'gainsboro':'#dcdcdc',
+ 'ghostwhite':'#f8f8ff',
+ 'gold':'#ffd700',
+ 'goldenrod':'#daa520',
+ 'gray':'#808080',
+ 'grey':'#808080',
+ 'green':'#008000',
+ 'greenyellow':'#adff2f',
+ 'honeydew':'#f0fff0',
+ 'hotpink':'#ff69b4',
+ 'indianred':'#cd5c5c',
+ 'indigo':'#4b0082',
+ 'ivory':'#fffff0',
+ 'khaki':'#f0e68c',
+ 'lavender':'#e6e6fa',
+ 'lavenderblush':'#fff0f5',
+ 'lawngreen':'#7cfc00',
+ 'lemonchiffon':'#fffacd',
+ 'lightblue':'#add8e6',
+ 'lightcoral':'#f08080',
+ 'lightcyan':'#e0ffff',
+ 'lightgoldenrodyellow':'#fafad2',
+ 'lightgray':'#d3d3d3',
+ 'lightgrey':'#d3d3d3',
+ 'lightgreen':'#90ee90',
+ 'lightpink':'#ffb6c1',
+ 'lightsalmon':'#ffa07a',
+ 'lightseagreen':'#20b2aa',
+ 'lightskyblue':'#87cefa',
+ 'lightslategray':'#778899',
+ 'lightslategrey':'#778899',
+ 'lightsteelblue':'#b0c4de',
+ 'lightyellow':'#ffffe0',
+ 'lime':'#00ff00',
+ 'limegreen':'#32cd32',
+ 'linen':'#faf0e6',
+ 'magenta':'#ff00ff',
+ 'maroon':'#800000',
+ 'mediumaquamarine':'#66cdaa',
+ 'mediumblue':'#0000cd',
+ 'mediumorchid':'#ba55d3',
+ 'mediumpurple':'#9370d8',
+ 'mediumseagreen':'#3cb371',
+ 'mediumslateblue':'#7b68ee',
+ 'mediumspringgreen':'#00fa9a',
+ 'mediumturquoise':'#48d1cc',
+ 'mediumvioletred':'#c71585',
+ 'midnightblue':'#191970',
+ 'mintcream':'#f5fffa',
+ 'mistyrose':'#ffe4e1',
+ 'moccasin':'#ffe4b5',
+ 'navajowhite':'#ffdead',
+ 'navy':'#000080',
+ 'oldlace':'#fdf5e6',
+ 'olive':'#808000',
+ 'olivedrab':'#6b8e23',
+ 'orange':'#ffa500',
+ 'orangered':'#ff4500',
+ 'orchid':'#da70d6',
+ 'palegoldenrod':'#eee8aa',
+ 'palegreen':'#98fb98',
+ 'paleturquoise':'#afeeee',
+ 'palevioletred':'#d87093',
+ 'papayawhip':'#ffefd5',
+ 'peachpuff':'#ffdab9',
+ 'peru':'#cd853f',
+ 'pink':'#ffc0cb',
+ 'plum':'#dda0dd',
+ 'powderblue':'#b0e0e6',
+ 'purple':'#800080',
+ 'rebeccapurple':'#663399',
+ 'red':'#ff0000',
+ 'rosybrown':'#bc8f8f',
+ 'royalblue':'#4169e1',
+ 'saddlebrown':'#8b4513',
+ 'salmon':'#fa8072',
+ 'sandybrown':'#f4a460',
+ 'seagreen':'#2e8b57',
+ 'seashell':'#fff5ee',
+ 'sienna':'#a0522d',
+ 'silver':'#c0c0c0',
+ 'skyblue':'#87ceeb',
+ 'slateblue':'#6a5acd',
+ 'slategray':'#708090',
+ 'slategrey':'#708090',
+ 'snow':'#fffafa',
+ 'springgreen':'#00ff7f',
+ 'steelblue':'#4682b4',
+ 'tan':'#d2b48c',
+ 'teal':'#008080',
+ 'thistle':'#d8bfd8',
+ 'tomato':'#ff6347',
+ 'turquoise':'#40e0d0',
+ 'violet':'#ee82ee',
+ 'wheat':'#f5deb3',
+ 'white':'#ffffff',
+ 'whitesmoke':'#f5f5f5',
+ 'yellow':'#ffff00',
+ 'yellowgreen':'#9acd32'
+};
+},{}],12:[function(require,module,exports){
+module.exports = {
+ colors: require("./colors"),
+ unitConversions: require("./unit-conversions")
+};
+
+},{"./colors":11,"./unit-conversions":13}],13:[function(require,module,exports){
+module.exports = {
+ length: {
+ 'm': 1,
+ 'cm': 0.01,
+ 'mm': 0.001,
+ 'in': 0.0254,
+ 'px': 0.0254 / 96,
+ 'pt': 0.0254 / 72,
+ 'pc': 0.0254 / 72 * 12
+ },
+ duration: {
+ 's': 1,
+ 'ms': 0.001
+ },
+ angle: {
+ 'rad': 1 / (2 * Math.PI),
+ 'deg': 1 / 360,
+ 'grad': 1 / 400,
+ 'turn': 1
+ }
+};
+},{}],14:[function(require,module,exports){
+var abstractFileManager = function() {
+};
+
+abstractFileManager.prototype.getPath = function (filename) {
+ var j = filename.lastIndexOf('?');
+ if (j > 0) {
+ filename = filename.slice(0, j);
+ }
+ j = filename.lastIndexOf('/');
+ if (j < 0) {
+ j = filename.lastIndexOf('\\');
+ }
+ if (j < 0) {
+ return "";
+ }
+ return filename.slice(0, j + 1);
+};
+
+abstractFileManager.prototype.tryAppendLessExtension = function(path) {
+ return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + '.less';
+};
+
+abstractFileManager.prototype.supportsSync = function() {
+ return false;
+};
+
+abstractFileManager.prototype.alwaysMakePathsAbsolute = function() {
+ return false;
+};
+
+abstractFileManager.prototype.isPathAbsolute = function(filename) {
+ return (/^(?:[a-z-]+:|\/|\\|#)/i).test(filename);
+};
+
+abstractFileManager.prototype.join = function(basePath, laterPath) {
+ if (!basePath) {
+ return laterPath;
+ }
+ return basePath + laterPath;
+};
+abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
+ // diff between two paths to create a relative path
+
+ var urlParts = this.extractUrlParts(url),
+ baseUrlParts = this.extractUrlParts(baseUrl),
+ i, max, urlDirectories, baseUrlDirectories, diff = "";
+ if (urlParts.hostPart !== baseUrlParts.hostPart) {
+ return "";
+ }
+ max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
+ for (i = 0; i < max; i++) {
+ if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
+ }
+ baseUrlDirectories = baseUrlParts.directories.slice(i);
+ urlDirectories = urlParts.directories.slice(i);
+ for (i = 0; i < baseUrlDirectories.length - 1; i++) {
+ diff += "../";
+ }
+ for (i = 0; i < urlDirectories.length - 1; i++) {
+ diff += urlDirectories[i] + "/";
+ }
+ return diff;
+};
+// helper function, not part of API
+abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) {
+ // urlParts[1] = protocol&hostname || /
+ // urlParts[2] = / if path relative to host base
+ // urlParts[3] = directories
+ // urlParts[4] = filename
+ // urlParts[5] = parameters
+
+ var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
+ urlParts = url.match(urlPartsRegex),
+ returner = {}, directories = [], i, baseUrlParts;
+
+ if (!urlParts) {
+ throw new Error("Could not parse sheet href - '" + url + "'");
+ }
+
+ // Stylesheets in IE don't always return the full path
+ if (baseUrl && (!urlParts[1] || urlParts[2])) {
+ baseUrlParts = baseUrl.match(urlPartsRegex);
+ if (!baseUrlParts) {
+ throw new Error("Could not parse page url - '" + baseUrl + "'");
+ }
+ urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
+ if (!urlParts[2]) {
+ urlParts[3] = baseUrlParts[3] + urlParts[3];
+ }
+ }
+
+ if (urlParts[3]) {
+ directories = urlParts[3].replace(/\\/g, "/").split("/");
+
+ // extract out . before .. so .. doesn't absorb a non-directory
+ for (i = 0; i < directories.length; i++) {
+ if (directories[i] === ".") {
+ directories.splice(i, 1);
+ i -= 1;
+ }
+ }
+
+ for (i = 0; i < directories.length; i++) {
+ if (directories[i] === ".." && i > 0) {
+ directories.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+
+ returner.hostPart = urlParts[1];
+ returner.directories = directories;
+ returner.path = (urlParts[1] || "") + directories.join("/");
+ returner.fileUrl = returner.path + (urlParts[4] || "");
+ returner.url = returner.fileUrl + (urlParts[5] || "");
+ return returner;
+};
+
+module.exports = abstractFileManager;
+
+},{}],15:[function(require,module,exports){
+var logger = require("../logger");
+var environment = function(externalEnvironment, fileManagers) {
+ this.fileManagers = fileManagers || [];
+ externalEnvironment = externalEnvironment || {};
+
+ var optionalFunctions = ["encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"],
+ requiredFunctions = [],
+ functions = requiredFunctions.concat(optionalFunctions);
+
+ for (var i = 0; i < functions.length; i++) {
+ var propName = functions[i],
+ environmentFunc = externalEnvironment[propName];
+ if (environmentFunc) {
+ this[propName] = environmentFunc.bind(externalEnvironment);
+ } else if (i < requiredFunctions.length) {
+ this.warn("missing required function in environment - " + propName);
+ }
+ }
+};
+
+environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) {
+
+ if (!filename) {
+ logger.warn("getFileManager called with no filename.. Please report this issue. continuing.");
+ }
+ if (currentDirectory == null) {
+ logger.warn("getFileManager called with null directory.. Please report this issue. continuing.");
+ }
+
+ var fileManagers = this.fileManagers;
+ if (options.pluginManager) {
+ fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers());
+ }
+ for (var i = fileManagers.length - 1; i >= 0 ; i--) {
+ var fileManager = fileManagers[i];
+ if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) {
+ return fileManager;
+ }
+ }
+ return null;
+};
+
+environment.prototype.addFileManager = function (fileManager) {
+ this.fileManagers.push(fileManager);
+};
+
+environment.prototype.clearFileManagers = function () {
+ this.fileManagers = [];
+};
+
+module.exports = environment;
+
+},{"../logger":31}],16:[function(require,module,exports){
+var Color = require("../tree/color"),
+ functionRegistry = require("./function-registry");
+
+// Color Blending
+// ref: http://www.w3.org/TR/compositing-1
+
+function colorBlend(mode, color1, color2) {
+ var ab = color1.alpha, cb, // backdrop
+ as = color2.alpha, cs, // source
+ ar, cr, r = []; // result
+
+ ar = as + ab * (1 - as);
+ for (var i = 0; i < 3; i++) {
+ cb = color1.rgb[i] / 255;
+ cs = color2.rgb[i] / 255;
+ cr = mode(cb, cs);
+ if (ar) {
+ cr = (as * cs + ab * (cb -
+ as * (cb + cs - cr))) / ar;
+ }
+ r[i] = cr * 255;
+ }
+
+ return new Color(r, ar);
+}
+
+var colorBlendModeFunctions = {
+ multiply: function(cb, cs) {
+ return cb * cs;
+ },
+ screen: function(cb, cs) {
+ return cb + cs - cb * cs;
+ },
+ overlay: function(cb, cs) {
+ cb *= 2;
+ return (cb <= 1) ?
+ colorBlendModeFunctions.multiply(cb, cs) :
+ colorBlendModeFunctions.screen(cb - 1, cs);
+ },
+ softlight: function(cb, cs) {
+ var d = 1, e = cb;
+ if (cs > 0.5) {
+ e = 1;
+ d = (cb > 0.25) ? Math.sqrt(cb)
+ : ((16 * cb - 12) * cb + 4) * cb;
+ }
+ return cb - (1 - 2 * cs) * e * (d - cb);
+ },
+ hardlight: function(cb, cs) {
+ return colorBlendModeFunctions.overlay(cs, cb);
+ },
+ difference: function(cb, cs) {
+ return Math.abs(cb - cs);
+ },
+ exclusion: function(cb, cs) {
+ return cb + cs - 2 * cb * cs;
+ },
+
+ // non-w3c functions:
+ average: function(cb, cs) {
+ return (cb + cs) / 2;
+ },
+ negation: function(cb, cs) {
+ return 1 - Math.abs(cb + cs - 1);
+ }
+};
+
+for (var f in colorBlendModeFunctions) {
+ if (colorBlendModeFunctions.hasOwnProperty(f)) {
+ colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]);
+ }
+}
+
+functionRegistry.addMultiple(colorBlend);
+
+},{"../tree/color":47,"./function-registry":21}],17:[function(require,module,exports){
+var Dimension = require("../tree/dimension"),
+ Color = require("../tree/color"),
+ Quoted = require("../tree/quoted"),
+ Anonymous = require("../tree/anonymous"),
+ functionRegistry = require("./function-registry"),
+ colorFunctions;
+
+function clamp(val) {
+ return Math.min(1, Math.max(0, val));
+}
+function hsla(color) {
+ return colorFunctions.hsla(color.h, color.s, color.l, color.a);
+}
+function number(n) {
+ if (n instanceof Dimension) {
+ return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
+ } else if (typeof n === 'number') {
+ return n;
+ } else {
+ throw {
+ type: "Argument",
+ message: "color functions take numbers as parameters"
+ };
+ }
+}
+function scaled(n, size) {
+ if (n instanceof Dimension && n.unit.is('%')) {
+ return parseFloat(n.value * size / 100);
+ } else {
+ return number(n);
+ }
+}
+colorFunctions = {
+ rgb: function (r, g, b) {
+ return colorFunctions.rgba(r, g, b, 1.0);
+ },
+ rgba: function (r, g, b, a) {
+ var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
+ a = number(a);
+ return new Color(rgb, a);
+ },
+ hsl: function (h, s, l) {
+ return colorFunctions.hsla(h, s, l, 1.0);
+ },
+ hsla: function (h, s, l, a) {
+ function hue(h) {
+ h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
+ if (h * 6 < 1) {
+ return m1 + (m2 - m1) * h * 6;
+ }
+ else if (h * 2 < 1) {
+ return m2;
+ }
+ else if (h * 3 < 2) {
+ return m1 + (m2 - m1) * (2 / 3 - h) * 6;
+ }
+ else {
+ return m1;
+ }
+ }
+
+ h = (number(h) % 360) / 360;
+ s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
+
+ var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
+ var m1 = l * 2 - m2;
+
+ return colorFunctions.rgba(hue(h + 1 / 3) * 255,
+ hue(h) * 255,
+ hue(h - 1 / 3) * 255,
+ a);
+ },
+
+ hsv: function(h, s, v) {
+ return colorFunctions.hsva(h, s, v, 1.0);
+ },
+
+ hsva: function(h, s, v, a) {
+ h = ((number(h) % 360) / 360) * 360;
+ s = number(s); v = number(v); a = number(a);
+
+ var i, f;
+ i = Math.floor((h / 60) % 6);
+ f = (h / 60) - i;
+
+ var vs = [v,
+ v * (1 - s),
+ v * (1 - f * s),
+ v * (1 - (1 - f) * s)];
+ var perm = [[0, 3, 1],
+ [2, 0, 1],
+ [1, 0, 3],
+ [1, 2, 0],
+ [3, 1, 0],
+ [0, 1, 2]];
+
+ return colorFunctions.rgba(vs[perm[i][0]] * 255,
+ vs[perm[i][1]] * 255,
+ vs[perm[i][2]] * 255,
+ a);
+ },
+
+ hue: function (color) {
+ return new Dimension(color.toHSL().h);
+ },
+ saturation: function (color) {
+ return new Dimension(color.toHSL().s * 100, '%');
+ },
+ lightness: function (color) {
+ return new Dimension(color.toHSL().l * 100, '%');
+ },
+ hsvhue: function(color) {
+ return new Dimension(color.toHSV().h);
+ },
+ hsvsaturation: function (color) {
+ return new Dimension(color.toHSV().s * 100, '%');
+ },
+ hsvvalue: function (color) {
+ return new Dimension(color.toHSV().v * 100, '%');
+ },
+ red: function (color) {
+ return new Dimension(color.rgb[0]);
+ },
+ green: function (color) {
+ return new Dimension(color.rgb[1]);
+ },
+ blue: function (color) {
+ return new Dimension(color.rgb[2]);
+ },
+ alpha: function (color) {
+ return new Dimension(color.toHSL().a);
+ },
+ luma: function (color) {
+ return new Dimension(color.luma() * color.alpha * 100, '%');
+ },
+ luminance: function (color) {
+ var luminance =
+ (0.2126 * color.rgb[0] / 255) +
+ (0.7152 * color.rgb[1] / 255) +
+ (0.0722 * color.rgb[2] / 255);
+
+ return new Dimension(luminance * color.alpha * 100, '%');
+ },
+ saturate: function (color, amount) {
+ // filter: saturate(3.2);
+ // should be kept as is, so check for color
+ if (!color.rgb) {
+ return null;
+ }
+ var hsl = color.toHSL();
+
+ hsl.s += amount.value / 100;
+ hsl.s = clamp(hsl.s);
+ return hsla(hsl);
+ },
+ desaturate: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.s -= amount.value / 100;
+ hsl.s = clamp(hsl.s);
+ return hsla(hsl);
+ },
+ lighten: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.l += amount.value / 100;
+ hsl.l = clamp(hsl.l);
+ return hsla(hsl);
+ },
+ darken: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.l -= amount.value / 100;
+ hsl.l = clamp(hsl.l);
+ return hsla(hsl);
+ },
+ fadein: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.a += amount.value / 100;
+ hsl.a = clamp(hsl.a);
+ return hsla(hsl);
+ },
+ fadeout: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.a -= amount.value / 100;
+ hsl.a = clamp(hsl.a);
+ return hsla(hsl);
+ },
+ fade: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.a = amount.value / 100;
+ hsl.a = clamp(hsl.a);
+ return hsla(hsl);
+ },
+ spin: function (color, amount) {
+ var hsl = color.toHSL();
+ var hue = (hsl.h + amount.value) % 360;
+
+ hsl.h = hue < 0 ? 360 + hue : hue;
+
+ return hsla(hsl);
+ },
+ //
+ // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
+ // http://sass-lang.com
+ //
+ mix: function (color1, color2, weight) {
+ if (!weight) {
+ weight = new Dimension(50);
+ }
+ var p = weight.value / 100.0;
+ var w = p * 2 - 1;
+ var a = color1.toHSL().a - color2.toHSL().a;
+
+ var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
+ var w2 = 1 - w1;
+
+ var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
+ color1.rgb[1] * w1 + color2.rgb[1] * w2,
+ color1.rgb[2] * w1 + color2.rgb[2] * w2];
+
+ var alpha = color1.alpha * p + color2.alpha * (1 - p);
+
+ return new Color(rgb, alpha);
+ },
+ greyscale: function (color) {
+ return colorFunctions.desaturate(color, new Dimension(100));
+ },
+ contrast: function (color, dark, light, threshold) {
+ // filter: contrast(3.2);
+ // should be kept as is, so check for color
+ if (!color.rgb) {
+ return null;
+ }
+ if (typeof light === 'undefined') {
+ light = colorFunctions.rgba(255, 255, 255, 1.0);
+ }
+ if (typeof dark === 'undefined') {
+ dark = colorFunctions.rgba(0, 0, 0, 1.0);
+ }
+ //Figure out which is actually light and dark!
+ if (dark.luma() > light.luma()) {
+ var t = light;
+ light = dark;
+ dark = t;
+ }
+ if (typeof threshold === 'undefined') {
+ threshold = 0.43;
+ } else {
+ threshold = number(threshold);
+ }
+ if (color.luma() < threshold) {
+ return light;
+ } else {
+ return dark;
+ }
+ },
+ argb: function (color) {
+ return new Anonymous(color.toARGB());
+ },
+ color: function(c) {
+ if ((c instanceof Quoted) &&
+ (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
+ return new Color(c.value.slice(1));
+ }
+ if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
+ c.value = undefined;
+ return c;
+ }
+ throw {
+ type: "Argument",
+ message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF"
+ };
+ },
+ tint: function(color, amount) {
+ return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
+ },
+ shade: function(color, amount) {
+ return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
+ }
+};
+functionRegistry.addMultiple(colorFunctions);
+
+},{"../tree/anonymous":43,"../tree/color":47,"../tree/dimension":53,"../tree/quoted":70,"./function-registry":21}],18:[function(require,module,exports){
+module.exports = function(environment) {
+ var Quoted = require("../tree/quoted"),
+ URL = require("../tree/url"),
+ functionRegistry = require("./function-registry"),
+ fallback = function(functionThis, node) {
+ return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context);
+ },
+ logger = require('../logger');
+
+ functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) {
+
+ if (!filePathNode) {
+ filePathNode = mimetypeNode;
+ mimetypeNode = null;
+ }
+
+ var mimetype = mimetypeNode && mimetypeNode.value;
+ var filePath = filePathNode.value;
+ var currentFileInfo = this.currentFileInfo;
+ var currentDirectory = currentFileInfo.relativeUrls ?
+ currentFileInfo.currentDirectory : currentFileInfo.entryPath;
+
+ var fragmentStart = filePath.indexOf('#');
+ var fragment = '';
+ if (fragmentStart !== -1) {
+ fragment = filePath.slice(fragmentStart);
+ filePath = filePath.slice(0, fragmentStart);
+ }
+
+ var fileManager = environment.getFileManager(filePath, currentDirectory, this.context, environment, true);
+
+ if (!fileManager) {
+ return fallback(this, filePathNode);
+ }
+
+ var useBase64 = false;
+
+ // detect the mimetype if not given
+ if (!mimetypeNode) {
+
+ mimetype = environment.mimeLookup(filePath);
+
+ if (mimetype === "image/svg+xml") {
+ useBase64 = false;
+ } else {
+ // use base 64 unless it's an ASCII or UTF-8 format
+ var charset = environment.charsetLookup(mimetype);
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
+ }
+ if (useBase64) { mimetype += ';base64'; }
+ }
+ else {
+ useBase64 = /;base64$/.test(mimetype);
+ }
+
+ var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
+ if (!fileSync.contents) {
+ logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
+ return fallback(this, filePathNode || mimetypeNode);
+ }
+ var buf = fileSync.contents;
+ if (useBase64 && !environment.encodeBase64) {
+ return fallback(this, filePathNode);
+ }
+
+ buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
+
+ var uri = "data:" + mimetype + ',' + buf + fragment;
+
+ // IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
+ // and the --ieCompat flag is enabled, return a normal url() instead.
+ var DATA_URI_MAX = 32768;
+ if (uri.length >= DATA_URI_MAX) {
+
+ if (this.context.ieCompat !== false) {
+ logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length +
+ " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
+
+ return fallback(this, filePathNode || mimetypeNode);
+ }
+ }
+
+ return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
+ });
+};
+
+},{"../logger":31,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],19:[function(require,module,exports){
+var Keyword = require("../tree/keyword"),
+ functionRegistry = require("./function-registry");
+
+var defaultFunc = {
+ eval: function () {
+ var v = this.value_, e = this.error_;
+ if (e) {
+ throw e;
+ }
+ if (v != null) {
+ return v ? Keyword.True : Keyword.False;
+ }
+ },
+ value: function (v) {
+ this.value_ = v;
+ },
+ error: function (e) {
+ this.error_ = e;
+ },
+ reset: function () {
+ this.value_ = this.error_ = null;
+ }
+};
+
+functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
+
+module.exports = defaultFunc;
+
+},{"../tree/keyword":62,"./function-registry":21}],20:[function(require,module,exports){
+var functionRegistry = require("./function-registry");
+
+var functionCaller = function(name, context, index, currentFileInfo) {
+ this.name = name.toLowerCase();
+ this.func = functionRegistry.get(this.name);
+ this.index = index;
+ this.context = context;
+ this.currentFileInfo = currentFileInfo;
+};
+functionCaller.prototype.isValid = function() {
+ return Boolean(this.func);
+};
+functionCaller.prototype.call = function(args) {
+ return this.func.apply(this, args);
+};
+
+module.exports = functionCaller;
+
+},{"./function-registry":21}],21:[function(require,module,exports){
+module.exports = {
+ _data: {},
+ add: function(name, func) {
+ if (this._data.hasOwnProperty(name)) {
+ //TODO warn
+ }
+ this._data[name] = func;
+ },
+ addMultiple: function(functions) {
+ Object.keys(functions).forEach(
+ function(name) {
+ this.add(name, functions[name]);
+ }.bind(this));
+ },
+ get: function(name) {
+ return this._data[name];
+ }
+};
+
+},{}],22:[function(require,module,exports){
+module.exports = function(environment) {
+ var functions = {
+ functionRegistry: require("./function-registry"),
+ functionCaller: require("./function-caller")
+ };
+
+ //register functions
+ require("./default");
+ require("./color");
+ require("./color-blending");
+ require("./data-uri")(environment);
+ require("./math");
+ require("./number");
+ require("./string");
+ require("./svg")(environment);
+ require("./types");
+
+ return functions;
+};
+
+},{"./color":17,"./color-blending":16,"./data-uri":18,"./default":19,"./function-caller":20,"./function-registry":21,"./math":23,"./number":24,"./string":25,"./svg":26,"./types":27}],23:[function(require,module,exports){
+var Dimension = require("../tree/dimension"),
+ functionRegistry = require("./function-registry");
+
+var mathFunctions = {
+ // name, unit
+ ceil: null,
+ floor: null,
+ sqrt: null,
+ abs: null,
+ tan: "",
+ sin: "",
+ cos: "",
+ atan: "rad",
+ asin: "rad",
+ acos: "rad"
+};
+
+function _math(fn, unit, n) {
+ if (!(n instanceof Dimension)) {
+ throw { type: "Argument", message: "argument must be a number" };
+ }
+ if (unit == null) {
+ unit = n.unit;
+ } else {
+ n = n.unify();
+ }
+ return new Dimension(fn(parseFloat(n.value)), unit);
+}
+
+for (var f in mathFunctions) {
+ if (mathFunctions.hasOwnProperty(f)) {
+ mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]);
+ }
+}
+
+mathFunctions.round = function (n, f) {
+ var fraction = typeof f === "undefined" ? 0 : f.value;
+ return _math(function(num) { return num.toFixed(fraction); }, null, n);
+};
+
+functionRegistry.addMultiple(mathFunctions);
+
+},{"../tree/dimension":53,"./function-registry":21}],24:[function(require,module,exports){
+var Dimension = require("../tree/dimension"),
+ Anonymous = require("../tree/anonymous"),
+ functionRegistry = require("./function-registry");
+
+var minMax = function (isMin, args) {
+ args = Array.prototype.slice.call(args);
+ switch(args.length) {
+ case 0: throw { type: "Argument", message: "one or more arguments required" };
+ }
+ var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
+ order = [], // elems only contains original argument values.
+ values = {}; // key is the unit.toString() for unified Dimension values,
+ // value is the index into the order array.
+ for (i = 0; i < args.length; i++) {
+ current = args[i];
+ if (!(current instanceof Dimension)) {
+ if (Array.isArray(args[i].value)) {
+ Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
+ }
+ continue;
+ }
+ currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify();
+ unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
+ unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic;
+ unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
+ j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
+ if (j === undefined) {
+ if (unitStatic !== undefined && unit !== unitStatic) {
+ throw{ type: "Argument", message: "incompatible types" };
+ }
+ values[unit] = order.length;
+ order.push(current);
+ continue;
+ }
+ referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify();
+ if ( isMin && currentUnified.value < referenceUnified.value ||
+ !isMin && currentUnified.value > referenceUnified.value) {
+ order[j] = current;
+ }
+ }
+ if (order.length == 1) {
+ return order[0];
+ }
+ args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? "," : ", ");
+ return new Anonymous((isMin ? "min" : "max") + "(" + args + ")");
+};
+functionRegistry.addMultiple({
+ min: function () {
+ return minMax(true, arguments);
+ },
+ max: function () {
+ return minMax(false, arguments);
+ },
+ convert: function (val, unit) {
+ return val.convertTo(unit.value);
+ },
+ pi: function () {
+ return new Dimension(Math.PI);
+ },
+ mod: function(a, b) {
+ return new Dimension(a.value % b.value, a.unit);
+ },
+ pow: function(x, y) {
+ if (typeof x === "number" && typeof y === "number") {
+ x = new Dimension(x);
+ y = new Dimension(y);
+ } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) {
+ throw { type: "Argument", message: "arguments must be numbers" };
+ }
+
+ return new Dimension(Math.pow(x.value, y.value), x.unit);
+ },
+ percentage: function (n) {
+ return new Dimension(n.value * 100, '%');
+ }
+});
+
+},{"../tree/anonymous":43,"../tree/dimension":53,"./function-registry":21}],25:[function(require,module,exports){
+var Quoted = require("../tree/quoted"),
+ Anonymous = require("../tree/anonymous"),
+ JavaScript = require("../tree/javascript"),
+ functionRegistry = require("./function-registry");
+
+functionRegistry.addMultiple({
+ e: function (str) {
+ return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value);
+ },
+ escape: function (str) {
+ return new Anonymous(
+ encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B")
+ .replace(/\(/g, "%28").replace(/\)/g, "%29"));
+ },
+ replace: function (string, pattern, replacement, flags) {
+ var result = string.value;
+
+ result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value);
+ return new Quoted(string.quote || '', result, string.escaped);
+ },
+ '%': function (string /* arg, arg, ...*/) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ result = string.value;
+
+ for (var i = 0; i < args.length; i++) {
+ /*jshint loopfunc:true */
+ result = result.replace(/%[sda]/i, function(token) {
+ var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
+ return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
+ });
+ }
+ result = result.replace(/%%/g, '%');
+ return new Quoted(string.quote || '', result, string.escaped);
+ }
+});
+
+},{"../tree/anonymous":43,"../tree/javascript":60,"../tree/quoted":70,"./function-registry":21}],26:[function(require,module,exports){
+module.exports = function(environment) {
+ var Dimension = require("../tree/dimension"),
+ Color = require("../tree/color"),
+ Expression = require("../tree/expression"),
+ Quoted = require("../tree/quoted"),
+ URL = require("../tree/url"),
+ functionRegistry = require("./function-registry");
+
+ functionRegistry.add("svg-gradient", function(direction) {
+
+ function throwArgumentDescriptor() {
+ throw { type: "Argument",
+ message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
+ " end_color [end_position]" };
+ }
+
+ if (arguments.length < 3) {
+ throwArgumentDescriptor();
+ }
+ var stops = Array.prototype.slice.call(arguments, 1),
+ gradientDirectionSvg,
+ gradientType = "linear",
+ rectangleDimension = 'x="0" y="0" width="1" height="1"',
+ renderEnv = {compress: false},
+ returner,
+ directionValue = direction.toCSS(renderEnv),
+ i, color, position, positionValue, alpha;
+
+ switch (directionValue) {
+ case "to bottom":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
+ break;
+ case "to right":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
+ break;
+ case "to bottom right":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
+ break;
+ case "to top right":
+ gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
+ break;
+ case "ellipse":
+ case "ellipse at center":
+ gradientType = "radial";
+ gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
+ rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
+ break;
+ default:
+ throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right'," +
+ " 'to bottom right', 'to top right' or 'ellipse at center'" };
+ }
+ returner = '<?xml version="1.0" ?>' +
+ '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
+ '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
+
+ for (i = 0; i < stops.length; i+= 1) {
+ if (stops[i] instanceof Expression) {
+ color = stops[i].value[0];
+ position = stops[i].value[1];
+ } else {
+ color = stops[i];
+ position = undefined;
+ }
+
+ if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
+ throwArgumentDescriptor();
+ }
+ positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
+ alpha = color.alpha;
+ returner += '<stop offset="' + positionValue + '" stop-color="' + color.toRGB() + '"' + (alpha < 1 ? ' stop-opacity="' + alpha + '"' : '') + '/>';
+ }
+ returner += '</' + gradientType + 'Gradient>' +
+ '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
+
+ returner = encodeURIComponent(returner);
+
+ returner = "data:image/svg+xml," + returner;
+ return new URL(new Quoted("'" + returner + "'", returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
+ });
+};
+
+},{"../tree/color":47,"../tree/dimension":53,"../tree/expression":56,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],27:[function(require,module,exports){
+var Keyword = require("../tree/keyword"),
+ DetachedRuleset = require("../tree/detached-ruleset"),
+ Dimension = require("../tree/dimension"),
+ Color = require("../tree/color"),
+ Quoted = require("../tree/quoted"),
+ Anonymous = require("../tree/anonymous"),
+ URL = require("../tree/url"),
+ Operation = require("../tree/operation"),
+ functionRegistry = require("./function-registry");
+
+var isa = function (n, Type) {
+ return (n instanceof Type) ? Keyword.True : Keyword.False;
+ },
+ isunit = function (n, unit) {
+ if (unit === undefined) {
+ throw { type: "Argument", message: "missing the required second argument to isunit." };
+ }
+ unit = typeof unit.value === "string" ? unit.value : unit;
+ if (typeof unit !== "string") {
+ throw { type: "Argument", message: "Second argument to isunit should be a unit or a string." };
+ }
+ return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
+ },
+ getItemsFromNode = function(node) {
+ // handle non-array values as an array of length 1
+ // return 'undefined' if index is invalid
+ var items = Array.isArray(node.value) ?
+ node.value : Array(node);
+
+ return items.filter(function(item) {
+ if (item.type === "Comment") {
+ return false;
+ }
+ return true;
+ });
+ };
+functionRegistry.addMultiple({
+ isruleset: function (n) {
+ return isa(n, DetachedRuleset);
+ },
+ iscolor: function (n) {
+ return isa(n, Color);
+ },
+ isnumber: function (n) {
+ return isa(n, Dimension);
+ },
+ isstring: function (n) {
+ return isa(n, Quoted);
+ },
+ iskeyword: function (n) {
+ return isa(n, Keyword);
+ },
+ isurl: function (n) {
+ return isa(n, URL);
+ },
+ ispixel: function (n) {
+ return isunit(n, 'px');
+ },
+ ispercentage: function (n) {
+ return isunit(n, '%');
+ },
+ isem: function (n) {
+ return isunit(n, 'em');
+ },
+ isunit: isunit,
+ unit: function (val, unit) {
+ if (!(val instanceof Dimension)) {
+ throw { type: "Argument",
+ message: "the first argument to unit must be a number" +
+ (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
+ }
+ if (unit) {
+ if (unit instanceof Keyword) {
+ unit = unit.value;
+ } else {
+ unit = unit.toCSS();
+ }
+ } else {
+ unit = "";
+ }
+ return new Dimension(val.value, unit);
+ },
+ "get-unit": function (n) {
+ return new Anonymous(n.unit);
+ },
+ extract: function(values, index) {
+ index = index.value - 1; // (1-based index)
+
+ return getItemsFromNode(values)[index];
+ },
+ length: function(values) {
+ return new Dimension(getItemsFromNode(values).length);
+ }
+});
+
+},{"../tree/anonymous":43,"../tree/color":47,"../tree/detached-ruleset":52,"../tree/dimension":53,"../tree/keyword":62,"../tree/operation":68,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],28:[function(require,module,exports){
+var contexts = require("./contexts"),
+ Parser = require('./parser/parser');
+
+module.exports = function(environment) {
+
+ // FileInfo = {
+ // 'relativeUrls' - option - whether to adjust URL's to be relative
+ // 'filename' - full resolved filename of current file
+ // 'rootpath' - path to append to normal URLs for this node
+ // 'currentDirectory' - path to the current file, absolute
+ // 'rootFilename' - filename of the base file
+ // 'entryPath' - absolute path to the entry file
+ // 'reference' - whether the file should not be output and only output parts that are referenced
+
+ var ImportManager = function(context, rootFileInfo) {
+ this.rootFilename = rootFileInfo.filename;
+ this.paths = context.paths || []; // Search paths, when importing
+ this.contents = {}; // map - filename to contents of all the files
+ this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore
+ this.mime = context.mime;
+ this.error = null;
+ this.context = context;
+ // Deprecated? Unused outside of here, could be useful.
+ this.queue = []; // Files which haven't been imported yet
+ this.files = {}; // Holds the imported parse trees.
+ };
+ /**
+ * Add an import to be imported
+ * @param path - the raw path
+ * @param tryAppendLessExtension - whether to try appending the less extension (if the path has no extension)
+ * @param currentFileInfo - the current file info (used for instance to work out relative paths)
+ * @param importOptions - import options
+ * @param callback - callback for when it is imported
+ */
+ ImportManager.prototype.push = function (path, tryAppendLessExtension, currentFileInfo, importOptions, callback) {
+ var importManager = this;
+ this.queue.push(path);
+
+ var fileParsedFunc = function (e, root, fullPath) {
+ importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
+
+ var importedEqualsRoot = fullPath === importManager.rootFilename;
+ if (importOptions.optional && e) {
+ callback(null, {rules:[]}, false, null);
+ }
+ else {
+ importManager.files[fullPath] = root;
+ if (e && !importManager.error) { importManager.error = e; }
+ callback(e, root, importedEqualsRoot, fullPath);
+ }
+ };
+
+ var newFileInfo = {
+ relativeUrls: this.context.relativeUrls,
+ entryPath: currentFileInfo.entryPath,
+ rootpath: currentFileInfo.rootpath,
+ rootFilename: currentFileInfo.rootFilename
+ };
+
+ var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment);
+
+ if (!fileManager) {
+ fileParsedFunc({ message: "Could not find a file-manager for " + path });
+ return;
+ }
+
+ if (tryAppendLessExtension) {
+ path = fileManager.tryAppendLessExtension(path);
+ }
+
+ var loadFileCallback = function(loadedFile) {
+ var resolvedFilename = loadedFile.filename,
+ contents = loadedFile.contents.replace(/^\uFEFF/, '');
+
+ // Pass on an updated rootpath if path of imported file is relative and file
+ // is in a (sub|sup) directory
+ //
+ // Examples:
+ // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
+ // then rootpath should become 'less/module/nav/'
+ // - If path of imported file is '../mixins.less' and rootpath is 'less/',
+ // then rootpath should become 'less/../'
+ newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
+ if (newFileInfo.relativeUrls) {
+ newFileInfo.rootpath = fileManager.join(
+ (importManager.context.rootpath || ""),
+ fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
+
+ if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
+ newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
+ }
+ }
+ newFileInfo.filename = resolvedFilename;
+
+ var newEnv = new contexts.Parse(importManager.context);
+
+ newEnv.processImports = false;
+ importManager.contents[resolvedFilename] = contents;
+
+ if (currentFileInfo.reference || importOptions.reference) {
+ newFileInfo.reference = true;
+ }
+
+ if (importOptions.inline) {
+ fileParsedFunc(null, contents, resolvedFilename);
+ } else {
+ new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) {
+ fileParsedFunc(e, root, resolvedFilename);
+ });
+ }
+ };
+
+ var promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment,
+ function(err, loadedFile) {
+ if (err) {
+ fileParsedFunc(err);
+ } else {
+ loadFileCallback(loadedFile);
+ }
+ });
+ if (promise) {
+ promise.then(loadFileCallback, fileParsedFunc);
+ }
+ };
+ return ImportManager;
+};
+
+},{"./contexts":10,"./parser/parser":36}],29:[function(require,module,exports){
+module.exports = function(environment, fileManagers) {
+ var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
+
+ var less = {
+ version: [2, 4, 0],
+ data: require('./data'),
+ tree: require('./tree'),
+ Environment: (Environment = require("./environment/environment")),
+ AbstractFileManager: require("./environment/abstract-file-manager"),
+ environment: (environment = new Environment(environment, fileManagers)),
+ visitors: require('./visitors'),
+ Parser: require('./parser/parser'),
+ functions: require('./functions')(environment),
+ contexts: require("./contexts"),
+ SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)),
+ SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput, environment)),
+ ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)),
+ ImportManager: (ImportManager = require('./import-manager')(environment)),
+ render: require("./render")(environment, ParseTree, ImportManager),
+ parse: require("./parse")(environment, ParseTree, ImportManager),
+ LessError: require('./less-error'),
+ transformTree: require('./transform-tree'),
+ utils: require('./utils'),
+ PluginManager: require('./plugin-manager'),
+ logger: require('./logger')
+ };
+
+ return less;
+};
+
+},{"./contexts":10,"./data":12,"./environment/abstract-file-manager":14,"./environment/environment":15,"./functions":22,"./import-manager":28,"./less-error":30,"./logger":31,"./parse":33,"./parse-tree":32,"./parser/parser":36,"./plugin-manager":37,"./render":38,"./source-map-builder":39,"./source-map-output":40,"./transform-tree":41,"./tree":59,"./utils":80,"./visitors":84}],30:[function(require,module,exports){
+var utils = require("./utils");
+
+var LessError = module.exports = function LessError(e, importManager, currentFilename) {
+
+ Error.call(this);
+
+ var filename = e.filename || currentFilename;
+
+ if (importManager && filename) {
+ var input = importManager.contents[filename],
+ loc = utils.getLocation(e.index, input),
+ line = loc.line,
+ col = loc.column,
+ callLine = e.call && utils.getLocation(e.call, input).line,
+ lines = input.split('\n');
+
+ this.type = e.type || 'Syntax';
+ this.filename = filename;
+ this.index = e.index;
+ this.line = typeof line === 'number' ? line + 1 : null;
+ this.callLine = callLine + 1;
+ this.callExtract = lines[callLine];
+ this.column = col;
+ this.extract = [
+ lines[line - 1],
+ lines[line],
+ lines[line + 1]
+ ];
+ }
+ this.message = e.message;
+ this.stack = e.stack;
+};
+
+if (typeof Object.create === 'undefined') {
+ var F = function () {};
+ F.prototype = Error.prototype;
+ LessError.prototype = new F();
+} else {
+ LessError.prototype = Object.create(Error.prototype);
+}
+
+LessError.prototype.constructor = LessError;
+
+},{"./utils":80}],31:[function(require,module,exports){
+module.exports = {
+ error: function(msg) {
+ this._fireEvent("error", msg);
+ },
+ warn: function(msg) {
+ this._fireEvent("warn", msg);
+ },
+ info: function(msg) {
+ this._fireEvent("info", msg);
+ },
+ debug: function(msg) {
+ this._fireEvent("debug", msg);
+ },
+ addListener: function(listener) {
+ this._listeners.push(listener);
+ },
+ removeListener: function(listener) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ if (this._listeners[i] === listener) {
+ this._listeners.splice(i, 1);
+ return;
+ }
+ }
+ },
+ _fireEvent: function(type, msg) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ var logFunction = this._listeners[i][type];
+ if (logFunction) {
+ logFunction(msg);
+ }
+ }
+ },
+ _listeners: []
+};
+
+},{}],32:[function(require,module,exports){
+var LessError = require('./less-error'),
+ transformTree = require("./transform-tree"),
+ logger = require("./logger");
+
+module.exports = function(SourceMapBuilder) {
+ var ParseTree = function(root, imports) {
+ this.root = root;
+ this.imports = imports;
+ };
+
+ ParseTree.prototype.toCSS = function(options) {
+ var evaldRoot, result = {}, sourceMapBuilder;
+ try {
+ evaldRoot = transformTree(this.root, options);
+ } catch (e) {
+ throw new LessError(e, this.imports);
+ }
+
+ try {
+ var compress = Boolean(options.compress);
+ if (compress) {
+ logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
+ }
+
+ var toCSSOptions = {
+ compress: compress,
+ dumpLineNumbers: options.dumpLineNumbers,
+ strictUnits: Boolean(options.strictUnits),
+ numPrecision: 8};
+
+ if (options.sourceMap) {
+ sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
+ result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
+ } else {
+ result.css = evaldRoot.toCSS(toCSSOptions);
+ }
+ } catch (e) {
+ throw new LessError(e, this.imports);
+ }
+
+ if (options.pluginManager) {
+ var postProcessors = options.pluginManager.getPostProcessors();
+ for (var i = 0; i < postProcessors.length; i++) {
+ result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports });
+ }
+ }
+ if (options.sourceMap) {
+ result.map = sourceMapBuilder.getExternalSourceMap();
+ }
+
+ result.imports = [];
+ for (var file in this.imports.files) {
+ if (this.imports.files.hasOwnProperty(file) && file !== this.imports.rootFilename) {
+ result.imports.push(file);
+ }
+ }
+ return result;
+ };
+ return ParseTree;
+};
+
+},{"./less-error":30,"./logger":31,"./transform-tree":41}],33:[function(require,module,exports){
+var PromiseConstructor,
+ contexts = require("./contexts"),
+ Parser = require('./parser/parser'),
+ PluginManager = require('./plugin-manager');
+
+module.exports = function(environment, ParseTree, ImportManager) {
+ var parse = function (input, options, callback) {
+ options = options || {};
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ if (!callback) {
+ if (!PromiseConstructor) {
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
+ }
+ var self = this;
+ return new PromiseConstructor(function (resolve, reject) {
+ parse.call(self, input, options, function(err, output) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(output);
+ }
+ });
+ });
+ } else {
+ var context,
+ rootFileInfo,
+ pluginManager = new PluginManager(this);
+
+ pluginManager.addPlugins(options.plugins);
+ options.pluginManager = pluginManager;
+
+ context = new contexts.Parse(options);
+
+ if (options.rootFileInfo) {
+ rootFileInfo = options.rootFileInfo;
+ } else {
+ var filename = options.filename || "input";
+ var entryPath = filename.replace(/[^\/\\]*$/, "");
+ rootFileInfo = {
+ filename: filename,
+ relativeUrls: context.relativeUrls,
+ rootpath: context.rootpath || "",
+ currentDirectory: entryPath,
+ entryPath: entryPath,
+ rootFilename: filename
+ };
+ // add in a missing trailing slash
+ if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== "/") {
+ rootFileInfo.rootpath += "/";
+ }
+ }
+
+ var imports = new ImportManager(context, rootFileInfo);
+
+ new Parser(context, imports, rootFileInfo)
+ .parse(input, function (e, root) {
+ if (e) { return callback(e); }
+ callback(null, root, imports, options);
+ }, options);
+ }
+ };
+ return parse;
+};
+
+},{"./contexts":10,"./parser/parser":36,"./plugin-manager":37,"promise":undefined}],34:[function(require,module,exports){
+// Split the input into chunks.
+module.exports = function (input, fail) {
+ var len = input.length, level = 0, parenLevel = 0,
+ lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
+ chunks = [], emitFrom = 0,
+ chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
+
+ function emitChunk(force) {
+ var len = chunkerCurrentIndex - emitFrom;
+ if (((len < 512) && !force) || !len) {
+ return;
+ }
+ chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1));
+ emitFrom = chunkerCurrentIndex + 1;
+ }
+
+ for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
+ cc = input.charCodeAt(chunkerCurrentIndex);
+ if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
+ // a-z or whitespace
+ continue;
+ }
+
+ switch (cc) {
+ case 40: // (
+ parenLevel++;
+ lastOpeningParen = chunkerCurrentIndex;
+ continue;
+ case 41: // )
+ if (--parenLevel < 0) {
+ return fail("missing opening `(`", chunkerCurrentIndex);
+ }
+ continue;
+ case 59: // ;
+ if (!parenLevel) { emitChunk(); }
+ continue;
+ case 123: // {
+ level++;
+ lastOpening = chunkerCurrentIndex;
+ continue;
+ case 125: // }
+ if (--level < 0) {
+ return fail("missing opening `{`", chunkerCurrentIndex);
+ }
+ if (!level && !parenLevel) { emitChunk(); }
+ continue;
+ case 92: // \
+ if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; }
+ return fail("unescaped `\\`", chunkerCurrentIndex);
+ case 34:
+ case 39:
+ case 96: // ", ' and `
+ matched = 0;
+ currentChunkStartIndex = chunkerCurrentIndex;
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
+ if (cc2 > 96) { continue; }
+ if (cc2 == cc) { matched = 1; break; }
+ if (cc2 == 92) { // \
+ if (chunkerCurrentIndex == len - 1) {
+ return fail("unescaped `\\`", chunkerCurrentIndex);
+ }
+ chunkerCurrentIndex++;
+ }
+ }
+ if (matched) { continue; }
+ return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
+ case 47: // /, check for comment
+ if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; }
+ cc2 = input.charCodeAt(chunkerCurrentIndex + 1);
+ if (cc2 == 47) {
+ // //, find lnfeed
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
+ if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
+ }
+ } else if (cc2 == 42) {
+ // /*, find */
+ lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex;
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) {
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
+ if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; }
+ if (cc2 != 42) { continue; }
+ if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; }
+ }
+ if (chunkerCurrentIndex == len - 1) {
+ return fail("missing closing `*/`", currentChunkStartIndex);
+ }
+ chunkerCurrentIndex++;
+ }
+ continue;
+ case 42: // *, check for unmatched */
+ if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) {
+ return fail("unmatched `/*`", chunkerCurrentIndex);
+ }
+ continue;
+ }
+ }
+
+ if (level !== 0) {
+ if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
+ return fail("missing closing `}` or `*/`", lastOpening);
+ } else {
+ return fail("missing closing `}`", lastOpening);
+ }
+ } else if (parenLevel !== 0) {
+ return fail("missing closing `)`", lastOpeningParen);
+ }
+
+ emitChunk(true);
+ return chunks;
+};
+
+},{}],35:[function(require,module,exports){
+var chunker = require('./chunker');
+
+module.exports = function() {
+ var input, // LeSS input string
+ j, // current chunk
+ saveStack = [], // holds state for backtracking
+ furthest, // furthest index the parser has gone to
+ furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause
+ chunks, // chunkified input
+ current, // current chunk
+ currentPos, // index of current chunk, in `input`
+ parserInput = {};
+
+ parserInput.save = function() {
+ currentPos = parserInput.i;
+ saveStack.push( { current: current, i: parserInput.i, j: j });
+ };
+ parserInput.restore = function(possibleErrorMessage) {
+
+ if (parserInput.i > furthest || (parserInput.i === furthest && possibleErrorMessage && !furthestPossibleErrorMessage)) {
+ furthest = parserInput.i;
+ furthestPossibleErrorMessage = possibleErrorMessage;
+ }
+ var state = saveStack.pop();
+ current = state.current;
+ currentPos = parserInput.i = state.i;
+ j = state.j;
+ };
+ parserInput.forget = function() {
+ saveStack.pop();
+ };
+ function sync() {
+ if (parserInput.i > currentPos) {
+ current = current.slice(parserInput.i - currentPos);
+ currentPos = parserInput.i;
+ }
+ }
+ parserInput.isWhitespace = function (offset) {
+ var pos = parserInput.i + (offset || 0),
+ code = input.charCodeAt(pos);
+ return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF);
+ };
+ //
+ // Parse from a token, regexp or string, and move forward if match
+ //
+ parserInput.$ = function(tok) {
+ var tokType = typeof tok,
+ match, length;
+
+ // Either match a single character in the input,
+ // or match a regexp in the current chunk (`current`).
+ //
+ if (tokType === "string") {
+ if (input.charAt(parserInput.i) !== tok) {
+ return null;
+ }
+ skipWhitespace(1);
+ return tok;
+ }
+
+ // regexp
+ sync();
+ if (! (match = tok.exec(current))) {
+ return null;
+ }
+
+ length = match[0].length;
+
+ // The match is confirmed, add the match length to `i`,
+ // and consume any extra white-space characters (' ' || '\n')
+ // which come after that. The reason for this is that LeSS's
+ // grammar is mostly white-space insensitive.
+ //
+ skipWhitespace(length);
+
+ if (typeof match === 'string') {
+ return match;
+ } else {
+ return match.length === 1 ? match[0] : match;
+ }
+ };
+
+ // Specialization of $(tok)
+ parserInput.$re = function(tok) {
+ if (parserInput.i > currentPos) {
+ current = current.slice(parserInput.i - currentPos);
+ currentPos = parserInput.i;
+ }
+ var m = tok.exec(current);
+ if (!m) {
+ return null;
+ }
+
+ skipWhitespace(m[0].length);
+ if (typeof m === "string") {
+ return m;
+ }
+
+ return m.length === 1 ? m[0] : m;
+ };
+
+ // Specialization of $(tok)
+ parserInput.$char = function(tok) {
+ if (input.charAt(parserInput.i) !== tok) {
+ return null;
+ }
+ skipWhitespace(1);
+ return tok;
+ };
+
+ var CHARCODE_SPACE = 32,
+ CHARCODE_TAB = 9,
+ CHARCODE_LF = 10,
+ CHARCODE_CR = 13,
+ CHARCODE_PLUS = 43,
+ CHARCODE_COMMA = 44,
+ CHARCODE_FORWARD_SLASH = 47,
+ CHARCODE_9 = 57;
+
+ parserInput.autoCommentAbsorb = true;
+ parserInput.commentStore = [];
+ parserInput.finished = false;
+
+ var skipWhitespace = function(length) {
+ var oldi = parserInput.i, oldj = j,
+ curr = parserInput.i - currentPos,
+ endIndex = parserInput.i + current.length - curr,
+ mem = (parserInput.i += length),
+ inp = input,
+ c, nextChar, comment;
+
+ for (; parserInput.i < endIndex; parserInput.i++) {
+ c = inp.charCodeAt(parserInput.i);
+
+ if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
+ nextChar = inp.charAt(parserInput.i + 1);
+ if (nextChar === '/') {
+ comment = {index: parserInput.i, isLineComment: true};
+ var nextNewLine = inp.indexOf("\n", parserInput.i + 1);
+ if (nextNewLine < 0) {
+ nextNewLine = endIndex;
+ }
+ parserInput.i = nextNewLine;
+ comment.text = inp.substr(comment.i, parserInput.i - comment.i);
+ parserInput.commentStore.push(comment);
+ continue;
+ } else if (nextChar === '*') {
+ var haystack = inp.substr(parserInput.i);
+ var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//);
+ if (comment_search_result) {
+ comment = {
+ index: parserInput.i,
+ text: comment_search_result[0],
+ isLineComment: false
+ };
+ parserInput.i += comment.text.length - 1;
+ parserInput.commentStore.push(comment);
+ continue;
+ }
+ }
+ break;
+ }
+
+ if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
+ break;
+ }
+ }
+
+ current = current.slice(length + parserInput.i - mem + curr);
+ currentPos = parserInput.i;
+
+ if (!current.length) {
+ if (j < chunks.length - 1)
+ {
+ current = chunks[++j];
+ skipWhitespace(0); // skip space at the beginning of a chunk
+ return true; // things changed
+ }
+ parserInput.finished = true;
+ }
+
+ return oldi !== parserInput.i || oldj !== j;
+ };
+
+ // Same as $(), but don't change the state of the parser,
+ // just return the match.
+ parserInput.peek = function(tok) {
+ if (typeof tok === 'string') {
+ return input.charAt(parserInput.i) === tok;
+ } else {
+ return tok.test(current);
+ }
+ };
+
+ // Specialization of peek()
+ // TODO remove or change some currentChar calls to peekChar
+ parserInput.peekChar = function(tok) {
+ return input.charAt(parserInput.i) === tok;
+ };
+
+ parserInput.currentChar = function() {
+ return input.charAt(parserInput.i);
+ };
+
+ parserInput.getInput = function() {
+ return input;
+ };
+
+ parserInput.peekNotNumeric = function() {
+ var c = input.charCodeAt(parserInput.i);
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
+ return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
+ };
+
+ parserInput.start = function(str, chunkInput, failFunction) {
+ input = str;
+ parserInput.i = j = currentPos = furthest = 0;
+
+ // chunking apparantly makes things quicker (but my tests indicate
+ // it might actually make things slower in node at least)
+ // and it is a non-perfect parse - it can't recognise
+ // unquoted urls, meaning it can't distinguish comments
+ // meaning comments with quotes or {}() in them get 'counted'
+ // and then lead to parse errors.
+ // In addition if the chunking chunks in the wrong place we might
+ // not be able to parse a parser statement in one go
+ // this is officially deprecated but can be switched on via an option
+ // in the case it causes too much performance issues.
+ if (chunkInput) {
+ chunks = chunker(str, failFunction);
+ } else {
+ chunks = [str];
+ }
+
+ current = chunks[0];
+
+ skipWhitespace(0);
+ };
+
+ parserInput.end = function() {
+ var message,
+ isFinished = parserInput.i >= input.length;
+
+ if (parserInput.i < furthest) {
+ message = furthestPossibleErrorMessage;
+ parserInput.i = furthest;
+ }
+ return {
+ isFinished: isFinished,
+ furthest: parserInput.i,
+ furthestPossibleErrorMessage: message,
+ furthestReachedEnd: parserInput.i >= input.length - 1,
+ furthestChar: input[parserInput.i]
+ };
+ };
+
+ return parserInput;
+};
+
+},{"./chunker":34}],36:[function(require,module,exports){
+var LessError = require('../less-error'),
+ tree = require("../tree"),
+ visitors = require("../visitors"),
+ getParserInput = require("./parser-input"),
+ utils = require("../utils");
+
+//
+// less.js - parser
+//
+// A relatively straight-forward predictive parser.
+// There is no tokenization/lexing stage, the input is parsed
+// in one sweep.
+//
+// To make the parser fast enough to run in the browser, several
+// optimization had to be made:
+//
+// - Matching and slicing on a huge input is often cause of slowdowns.
+// The solution is to chunkify the input into smaller strings.
+// The chunks are stored in the `chunks` var,
+// `j` holds the current chunk index, and `currentPos` holds
+// the index of the current chunk in relation to `input`.
+// This gives us an almost 4x speed-up.
+//
+// - In many cases, we don't need to match individual tokens;
+// for example, if a value doesn't hold any variables, operations
+// or dynamic references, the parser can effectively 'skip' it,
+// treating it as a literal.
+// An example would be '1px solid #000' - which evaluates to itself,
+// we don't need to know what the individual components are.
+// The drawback, of course is that you don't get the benefits of
+// syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
+// and a smaller speed-up in the code-gen.
+//
+//
+// Token matching is done with the `$` function, which either takes
+// a terminal string or regexp, or a non-terminal function to call.
+// It also takes care of moving all the indices forwards.
+//
+//
+var Parser = function Parser(context, imports, fileInfo) {
+ var parsers,
+ parserInput = getParserInput();
+
+ function expect(arg, msg, index) {
+ // some older browsers return typeof 'function' for RegExp
+ var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg);
+ if (result) {
+ return result;
+ }
+ error(msg || (typeof arg === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'"
+ : "unexpected token"));
+ }
+
+ // Specialization of expect()
+ function expectChar(arg, msg) {
+ if (parserInput.$char(arg)) {
+ return arg;
+ }
+ error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'");
+ }
+
+ function error(msg, type) {
+ throw new LessError(
+ {
+ index: parserInput.i,
+ filename: fileInfo.filename,
+ type: type || 'Syntax',
+ message: msg
+ },
+ imports
+ );
+ }
+
+ function getDebugInfo(index) {
+ var filename = fileInfo.filename;
+
+ return {
+ lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1,
+ fileName: filename
+ };
+ }
+
+ //
+ // The Parser
+ //
+ return {
+
+ //
+ // Parse an input string into an abstract syntax tree,
+ // @param str A string containing 'less' markup
+ // @param callback call `callback` when done.
+ // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
+ //
+ parse: function (str, callback, additionalData) {
+ var root, error = null, globalVars, modifyVars, ignored, preText = "";
+
+ globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : '';
+ modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : '';
+
+ if (context.pluginManager) {
+ var preProcessors = context.pluginManager.getPreProcessors();
+ for (var i = 0; i < preProcessors.length; i++) {
+ str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo });
+ }
+ }
+
+ if (globalVars || (additionalData && additionalData.banner)) {
+ preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
+ ignored = imports.contentsIgnoredChars;
+ ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0;
+ ignored[fileInfo.filename] += preText.length;
+ }
+
+ str = str.replace(/\r\n?/g, '\n');
+ // Remove potential UTF Byte Order Mark
+ str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
+ imports.contents[fileInfo.filename] = str;
+
+ // Start with the primary rule.
+ // The whole syntax tree is held under a Ruleset node,
+ // with the `root` property set to true, so no `{}` are
+ // output. The callback is called when the input is parsed.
+ try {
+ parserInput.start(str, context.chunkInput, function fail(msg, index) {
+ throw LessError({
+ index: index,
+ type: 'Parse',
+ message: msg,
+ filename: fileInfo.filename
+ }, imports);
+ });
+
+ root = new(tree.Ruleset)(null, this.parsers.primary());
+ root.root = true;
+ root.firstRoot = true;
+ } catch (e) {
+ return callback(new LessError(e, imports, fileInfo.filename));
+ }
+
+ // If `i` is smaller than the `input.length - 1`,
+ // it means the parser wasn't able to parse the whole
+ // string, so we've got a parsing error.
+ //
+ // We try to extract a \n delimited string,
+ // showing the line where the parse error occurred.
+ // We split it up into two parts (the part which parsed,
+ // and the part which didn't), so we can color them differently.
+ var endInfo = parserInput.end();
+ if (!endInfo.isFinished) {
+
+ var message = endInfo.furthestPossibleErrorMessage;
+
+ if (!message) {
+ message = "Unrecognised input";
+ if (endInfo.furthestChar === '}') {
+ message += ". Possibly missing opening '{'";
+ } else if (endInfo.furthestChar === ')') {
+ message += ". Possibly missing opening '('";
+ } else if (endInfo.furthestReachedEnd) {
+ message += ". Possibly missing something";
+ }
+ }
+
+ error = new LessError({
+ type: "Parse",
+ message: message,
+ index: endInfo.furthest,
+ filename: fileInfo.filename
+ }, imports);
+ }
+
+ var finish = function (e) {
+ e = error || e || imports.error;
+
+ if (e) {
+ if (!(e instanceof LessError)) {
+ e = new LessError(e, imports, fileInfo.filename);
+ }
+
+ return callback(e);
+ }
+ else {
+ return callback(null, root);
+ }
+ };
+
+ if (context.processImports !== false) {
+ new visitors.ImportVisitor(imports, finish)
+ .run(root);
+ } else {
+ return finish();
+ }
+ },
+
+ //
+ // Here in, the parsing rules/functions
+ //
+ // The basic structure of the syntax tree generated is as follows:
+ //
+ // Ruleset -> Rule -> Value -> Expression -> Entity
+ //
+ // Here's some Less code:
+ //
+ // .class {
+ // color: #fff;
+ // border: 1px solid #000;
+ // width: @w + 4px;
+ // > .child {...}
+ // }
+ //
+ // And here's what the parse tree might look like:
+ //
+ // Ruleset (Selector '.class', [
+ // Rule ("color", Value ([Expression [Color #fff]]))
+ // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
+ // Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
+ // Ruleset (Selector [Element '>', '.child'], [...])
+ // ])
+ //
+ // In general, most rules will try to parse a token with the `$()` function, and if the return
+ // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
+ // first, before parsing, that's when we use `peek()`.
+ //
+ parsers: parsers = {
+ //
+ // The `primary` rule is the *entry* and *exit* point of the parser.
+ // The rules here can appear at any level of the parse tree.
+ //
+ // The recursive nature of the grammar is an interplay between the `block`
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
+ // as represented by this simplified grammar:
+ //
+ // primary → (ruleset | rule)+
+ // ruleset → selector+ block
+ // block → '{' primary '}'
+ //
+ // Only at one point is the primary rule not called from the
+ // block rule: at the root level.
+ //
+ primary: function () {
+ var mixin = this.mixin, root = [], node;
+
+ while (true)
+ {
+ while (true) {
+ node = this.comment();
+ if (!node) { break; }
+ root.push(node);
+ }
+ // always process comments before deciding if finished
+ if (parserInput.finished) {
+ break;
+ }
+ if (parserInput.peek('}')) {
+ break;
+ }
+
+ node = this.extendRule();
+ if (node) {
+ root = root.concat(node);
+ continue;
+ }
+
+ node = mixin.definition() || this.rule() || this.ruleset() ||
+ mixin.call() || this.rulesetCall() || this.directive();
+ if (node) {
+ root.push(node);
+ } else {
+ if (!(parserInput.$re(/^[\s\n]+/) || parserInput.$re(/^;+/))) {
+ break;
+ }
+ }
+ }
+
+ return root;
+ },
+
+ // comments are collected by the main parsing mechanism and then assigned to nodes
+ // where the current structure allows it
+ comment: function () {
+ if (parserInput.commentStore.length) {
+ var comment = parserInput.commentStore.shift();
+ return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo);
+ }
+ },
+
+ //
+ // Entities are tokens which can be found inside an Expression
+ //
+ entities: {
+ //
+ // A string, which supports escaping " and '
+ //
+ // "milky way" 'he\'s the one!'
+ //
+ quoted: function () {
+ var str, index = parserInput.i;
+
+ str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/);
+ if (str) {
+ return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, fileInfo);
+ }
+ },
+
+ //
+ // A catch-all word, such as:
+ //
+ // black border-collapse
+ //
+ keyword: function () {
+ var k = parserInput.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);
+ if (k) {
+ return tree.Color.fromKeyword(k) || new(tree.Keyword)(k);
+ }
+ },
+
+ //
+ // A function call
+ //
+ // rgb(255, 0, 255)
+ //
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
+ // deal with the details.
+ //
+ // The arguments are parsed with the `entities.arguments` parser.
+ //
+ call: function () {
+ var name, nameLC, args, alpha, index = parserInput.i;
+
+ if (parserInput.peek(/^url\(/i)) {
+ return;
+ }
+
+ parserInput.save();
+
+ name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/);
+ if (!name) { parserInput.forget(); return; }
+
+ name = name[1];
+ nameLC = name.toLowerCase();
+
+ if (nameLC === 'alpha') {
+ alpha = parsers.alpha();
+ if (alpha) {
+ return alpha;
+ }
+ }
+
+ args = this.arguments();
+
+ if (! parserInput.$char(')')) {
+ parserInput.restore("Could not parse call arguments or missing ')'");
+ return;
+ }
+
+ parserInput.forget();
+ return new(tree.Call)(name, args, index, fileInfo);
+ },
+ arguments: function () {
+ var args = [], arg;
+
+ while (true) {
+ arg = this.assignment() || parsers.expression();
+ if (!arg) {
+ break;
+ }
+ args.push(arg);
+ if (! parserInput.$char(',')) {
+ break;
+ }
+ }
+ return args;
+ },
+ literal: function () {
+ return this.dimension() ||
+ this.color() ||
+ this.quoted() ||
+ this.unicodeDescriptor();
+ },
+
+ // Assignments are argument entities for calls.
+ // They are present in ie filter properties as shown below.
+ //
+ // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
+ //
+
+ assignment: function () {
+ var key, value;
+ key = parserInput.$re(/^\w+(?=\s?=)/i);
+ if (!key) {
+ return;
+ }
+ if (!parserInput.$char('=')) {
+ return;
+ }
+ value = parsers.entity();
+ if (value) {
+ return new(tree.Assignment)(key, value);
+ }
+ },
+
+ //
+ // Parse url() tokens
+ //
+ // We use a specific rule for urls, because they don't really behave like
+ // standard function calls. The difference is that the argument doesn't have
+ // to be enclosed within a string, so it can't be parsed as an Expression.
+ //
+ url: function () {
+ var value, index = parserInput.i;
+
+ parserInput.autoCommentAbsorb = false;
+
+ if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) {
+ parserInput.autoCommentAbsorb = true;
+ return;
+ }
+
+ value = this.quoted() || this.variable() ||
+ parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
+
+ parserInput.autoCommentAbsorb = true;
+
+ expectChar(')');
+
+ return new(tree.URL)((value.value != null || value instanceof tree.Variable) ?
+ value : new(tree.Anonymous)(value), index, fileInfo);
+ },
+
+ //
+ // A Variable entity, such as `@fink`, in
+ //
+ // width: @fink + 2px
+ //
+ // We use a different parser for variable definitions,
+ // see `parsers.variable`.
+ //
+ variable: function () {
+ var name, index = parserInput.i;
+
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
+ return new(tree.Variable)(name, index, fileInfo);
+ }
+ },
+
+ // A variable entity useing the protective {} e.g. @{var}
+ variableCurly: function () {
+ var curly, index = parserInput.i;
+
+ if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
+ return new(tree.Variable)("@" + curly[1], index, fileInfo);
+ }
+ },
+
+ //
+ // A Hexadecimal color
+ //
+ // #4F3C2F
+ //
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
+ //
+ color: function () {
+ var rgb;
+
+ if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
+ // strip colons, brackets, whitespaces and other characters that should not
+ // definitely be part of color string
+ var colorCandidateString = rgb.input.match(/^#([\w]+).*/);
+ colorCandidateString = colorCandidateString[1];
+ if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
+ error("Invalid HEX color code");
+ }
+ return new(tree.Color)(rgb[1]);
+ }
+ },
+
+ //
+ // A Dimension, that is, a number and a unit
+ //
+ // 0.5em 95%
+ //
+ dimension: function () {
+ if (parserInput.peekNotNumeric()) {
+ return;
+ }
+
+ var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i);
+ if (value) {
+ return new(tree.Dimension)(value[1], value[2]);
+ }
+ },
+
+ //
+ // A unicode descriptor, as is used in unicode-range
+ //
+ // U+0?? or U+00A1-00A9
+ //
+ unicodeDescriptor: function () {
+ var ud;
+
+ ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
+ if (ud) {
+ return new(tree.UnicodeDescriptor)(ud[0]);
+ }
+ },
+
+ //
+ // JavaScript code to be evaluated
+ //
+ // `window.location.href`
+ //
+ javascript: function () {
+ var js, index = parserInput.i;
+
+ js = parserInput.$re(/^(~)?`([^`]*)`/);
+ if (js) {
+ return new(tree.JavaScript)(js[2], Boolean(js[1]), index, fileInfo);
+ }
+ }
+ },
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink:
+ //
+ variable: function () {
+ var name;
+
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
+ },
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink();
+ //
+ rulesetCall: function () {
+ var name;
+
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
+ return new tree.RulesetCall(name[1]);
+ }
+ },
+
+ //
+ // extend syntax - used to extend selectors
+ //
+ extend: function(isRule) {
+ var elements, e, index = parserInput.i, option, extendList, extend;
+
+ if (!(isRule ? parserInput.$re(/^&:extend\(/) : parserInput.$re(/^:extend\(/))) { return; }
+
+ do {
+ option = null;
+ elements = null;
+ while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
+ e = this.element();
+ if (!e) {
+ break;
+ }
+ if (elements) {
+ elements.push(e);
+ } else {
+ elements = [ e ];
+ }
+ }
+
+ option = option && option[1];
+ if (!elements) {
+ error("Missing target selector for :extend().");
+ }
+ extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
+ if (extendList) {
+ extendList.push(extend);
+ } else {
+ extendList = [ extend ];
+ }
+ } while (parserInput.$char(","));
+
+ expect(/^\)/);
+
+ if (isRule) {
+ expect(/^;/);
+ }
+
+ return extendList;
+ },
+
+ //
+ // extendRule - used in a rule to extend all the parent selectors
+ //
+ extendRule: function() {
+ return this.extend(true);
+ },
+
+ //
+ // Mixins
+ //
+ mixin: {
+ //
+ // A Mixin call, with an optional argument list
+ //
+ // #mixins > .square(#fff);
+ // .rounded(4px, black);
+ // .button;
+ //
+ // The `while` loop is there because mixins can be
+ // namespaced, but we only support the child and descendant
+ // selector for now.
+ //
+ call: function () {
+ var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex,
+ elements, elem, e, c, args;
+
+ if (s !== '.' && s !== '#') { return; }
+
+ parserInput.save(); // stop us absorbing part of an invalid selector
+
+ while (true) {
+ elemIndex = parserInput.i;
+ e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
+ if (!e) {
+ break;
+ }
+ elem = new(tree.Element)(c, e, elemIndex, fileInfo);
+ if (elements) {
+ elements.push(elem);
+ } else {
+ elements = [ elem ];
+ }
+ c = parserInput.$char('>');
+ }
+
+ if (elements) {
+ if (parserInput.$char('(')) {
+ args = this.args(true).args;
+ expectChar(')');
+ }
+
+ if (parsers.important()) {
+ important = true;
+ }
+
+ if (parsers.end()) {
+ parserInput.forget();
+ return new(tree.mixin.Call)(elements, args, index, fileInfo, important);
+ }
+ }
+
+ parserInput.restore();
+ },
+ args: function (isCall) {
+ var entities = parsers.entities,
+ returner = { args:null, variadic: false },
+ expressions = [], argsSemiColon = [], argsComma = [],
+ isSemiColonSeparated, expressionContainsNamed, name, nameLoop, value, arg;
+
+ parserInput.save();
+
+ while (true) {
+ if (isCall) {
+ arg = parsers.detachedRuleset() || parsers.expression();
+ } else {
+ parserInput.commentStore.length = 0;
+ if (parserInput.currentChar() === '.' && parserInput.$re(/^\.{3}/)) {
+ returner.variadic = true;
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
+ isSemiColonSeparated = true;
+ }
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
+ .push({ variadic: true });
+ break;
+ }
+ arg = entities.variable() || entities.literal() || entities.keyword();
+ }
+
+ if (!arg) {
+ break;
+ }
+
+ nameLoop = null;
+ if (arg.throwAwayComments) {
+ arg.throwAwayComments();
+ }
+ value = arg;
+ var val = null;
+
+ if (isCall) {
+ // Variable
+ if (arg.value && arg.value.length == 1) {
+ val = arg.value[0];
+ }
+ } else {
+ val = arg;
+ }
+
+ if (val && val instanceof tree.Variable) {
+ if (parserInput.$char(':')) {
+ if (expressions.length > 0) {
+ if (isSemiColonSeparated) {
+ error("Cannot mix ; and , as delimiter types");
+ }
+ expressionContainsNamed = true;
+ }
+
+ // we do not support setting a ruleset as a default variable - it doesn't make sense
+ // However if we do want to add it, there is nothing blocking it, just don't error
+ // and remove isCall dependency below
+ value = (isCall && parsers.detachedRuleset()) || parsers.expression();
+
+ if (!value) {
+ if (isCall) {
+ error("could not understand value for named argument");
+ } else {
+ parserInput.restore();
+ returner.args = [];
+ return returner;
+ }
+ }
+ nameLoop = (name = val.name);
+ } else if (!isCall && parserInput.$re(/^\.{3}/)) {
+ returner.variadic = true;
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
+ isSemiColonSeparated = true;
+ }
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
+ .push({ name: arg.name, variadic: true });
+ break;
+ } else if (!isCall) {
+ name = nameLoop = val.name;
+ value = null;
+ }
+ }
+
+ if (value) {
+ expressions.push(value);
+ }
+
+ argsComma.push({ name:nameLoop, value:value });
+
+ if (parserInput.$char(',')) {
+ continue;
+ }
+
+ if (parserInput.$char(';') || isSemiColonSeparated) {
+
+ if (expressionContainsNamed) {
+ error("Cannot mix ; and , as delimiter types");
+ }
+
+ isSemiColonSeparated = true;
+
+ if (expressions.length > 1) {
+ value = new(tree.Value)(expressions);
+ }
+ argsSemiColon.push({ name:name, value:value });
+
+ name = null;
+ expressions = [];
+ expressionContainsNamed = false;
+ }
+ }
+
+ parserInput.forget();
+ returner.args = isSemiColonSeparated ? argsSemiColon : argsComma;
+ return returner;
+ },
+ //
+ // A Mixin definition, with a list of parameters
+ //
+ // .rounded (@radius: 2px, @color) {
+ // ...
+ // }
+ //
+ // Until we have a finer grained state-machine, we have to
+ // do a look-ahead, to make sure we don't have a mixin call.
+ // See the `rule` function for more information.
+ //
+ // We start by matching `.rounded (`, and then proceed on to
+ // the argument list, which has optional default values.
+ // We store the parameters in `params`, with a `value` key,
+ // if there is a value, such as in the case of `@radius`.
+ //
+ // Once we've got our params list, and a closing `)`, we parse
+ // the `{...}` block.
+ //
+ definition: function () {
+ var name, params = [], match, ruleset, cond, variadic = false;
+ if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
+ parserInput.peek(/^[^{]*\}/)) {
+ return;
+ }
+
+ parserInput.save();
+
+ match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
+ if (match) {
+ name = match[1];
+
+ var argInfo = this.args(false);
+ params = argInfo.args;
+ variadic = argInfo.variadic;
+
+ // .mixincall("@{a}");
+ // looks a bit like a mixin definition..
+ // also
+ // .mixincall(@a: {rule: set;});
+ // so we have to be nice and restore
+ if (!parserInput.$char(')')) {
+ parserInput.restore("Missing closing ')'");
+ return;
+ }
+
+ parserInput.commentStore.length = 0;
+
+ if (parserInput.$re(/^when/)) { // Guard
+ cond = expect(parsers.conditions, 'expected condition');
+ }
+
+ ruleset = parsers.block();
+
+ if (ruleset) {
+ parserInput.forget();
+ return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
+ } else {
+ parserInput.restore();
+ }
+ } else {
+ parserInput.forget();
+ }
+ }
+ },
+
+ //
+ // Entities are the smallest recognized token,
+ // and can be found inside a rule's value.
+ //
+ entity: function () {
+ var entities = this.entities;
+
+ return this.comment() || entities.literal() || entities.variable() || entities.url() ||
+ entities.call() || entities.keyword() || entities.javascript();
+ },
+
+ //
+ // A Rule terminator. Note that we use `peek()` to check for '}',
+ // because the `block` rule will be expecting it, but we still need to make sure
+ // it's there, if ';' was ommitted.
+ //
+ end: function () {
+ return parserInput.$char(';') || parserInput.peek('}');
+ },
+
+ //
+ // IE's alpha function
+ //
+ // alpha(opacity=88)
+ //
+ alpha: function () {
+ var value;
+
+ if (! parserInput.$re(/^opacity=/i)) { return; }
+ value = parserInput.$re(/^\d+/);
+ if (!value) {
+ value = expect(this.entities.variable, "Could not parse alpha");
+ }
+ expectChar(')');
+ return new(tree.Alpha)(value);
+ },
+
+ //
+ // A Selector Element
+ //
+ // div
+ // + h1
+ // #socks
+ // input[type="text"]
+ //
+ // Elements are the building blocks for Selectors,
+ // they are made out of a `Combinator` (see combinator rule),
+ // and an element name, such as a tag a class, or `*`.
+ //
+ element: function () {
+ var e, c, v, index = parserInput.i;
+
+ c = this.combinator();
+
+ e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
+ parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
+ parserInput.$char('*') || parserInput.$char('&') || this.attribute() ||
+ parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
+ this.entities.variableCurly();
+
+ if (! e) {
+ parserInput.save();
+ if (parserInput.$char('(')) {
+ if ((v = this.selector()) && parserInput.$char(')')) {
+ e = new(tree.Paren)(v);
+ parserInput.forget();
+ } else {
+ parserInput.restore("Missing closing ')'");
+ }
+ } else {
+ parserInput.forget();
+ }
+ }
+
+ if (e) { return new(tree.Element)(c, e, index, fileInfo); }
+ },
+
+ //
+ // Combinators combine elements together, in a Selector.
+ //
+ // Because our parser isn't white-space sensitive, special care
+ // has to be taken, when parsing the descendant combinator, ` `,
+ // as it's an empty space. We have to check the previous character
+ // in the input, to see if it's a ` ` character. More info on how
+ // we deal with this in *combinator.js*.
+ //
+ combinator: function () {
+ var c = parserInput.currentChar();
+
+ if (c === '/') {
+ parserInput.save();
+ var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i);
+ if (slashedCombinator) {
+ parserInput.forget();
+ return new(tree.Combinator)(slashedCombinator);
+ }
+ parserInput.restore();
+ }
+
+ if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
+ parserInput.i++;
+ if (c === '^' && parserInput.currentChar() === '^') {
+ c = '^^';
+ parserInput.i++;
+ }
+ while (parserInput.isWhitespace()) { parserInput.i++; }
+ return new(tree.Combinator)(c);
+ } else if (parserInput.isWhitespace(-1)) {
+ return new(tree.Combinator)(" ");
+ } else {
+ return new(tree.Combinator)(null);
+ }
+ },
+ //
+ // A CSS selector (see selector below)
+ // with less extensions e.g. the ability to extend and guard
+ //
+ lessSelector: function () {
+ return this.selector(true);
+ },
+ //
+ // A CSS Selector
+ //
+ // .class > div + h1
+ // li a:hover
+ //
+ // Selectors are made out of one or more Elements, see above.
+ //
+ selector: function (isLess) {
+ var index = parserInput.i, elements, extendList, c, e, allExtends, when, condition;
+
+ while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$re(/^when/))) || (e = this.element())) {
+ if (when) {
+ condition = expect(this.conditions, 'expected condition');
+ } else if (condition) {
+ error("CSS guard can only be used at the end of selector");
+ } else if (extendList) {
+ if (allExtends) {
+ allExtends = allExtends.concat(extendList);
+ } else {
+ allExtends = extendList;
+ }
+ } else {
+ if (allExtends) { error("Extend can only be used at the end of selector"); }
+ c = parserInput.currentChar();
+ if (elements) {
+ elements.push(e);
+ } else {
+ elements = [ e ];
+ }
+ e = null;
+ }
+ if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
+ break;
+ }
+ }
+
+ if (elements) { return new(tree.Selector)(elements, allExtends, condition, index, fileInfo); }
+ if (allExtends) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
+ },
+ attribute: function () {
+ if (! parserInput.$char('[')) { return; }
+
+ var entities = this.entities,
+ key, val, op;
+
+ if (!(key = entities.variableCurly())) {
+ key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
+ }
+
+ op = parserInput.$re(/^[|~*$^]?=/);
+ if (op) {
+ val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly();
+ }
+
+ expectChar(']');
+
+ return new(tree.Attribute)(key, op, val);
+ },
+
+ //
+ // The `block` rule is used by `ruleset` and `mixin.definition`.
+ // It's a wrapper around the `primary` rule, with added `{}`.
+ //
+ block: function () {
+ var content;
+ if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) {
+ return content;
+ }
+ },
+
+ blockRuleset: function() {
+ var block = this.block();
+
+ if (block) {
+ block = new tree.Ruleset(null, block);
+ }
+ return block;
+ },
+
+ detachedRuleset: function() {
+ var blockRuleset = this.blockRuleset();
+ if (blockRuleset) {
+ return new tree.DetachedRuleset(blockRuleset);
+ }
+ },
+
+ //
+ // div, .class, body > p {...}
+ //
+ ruleset: function () {
+ var selectors, s, rules, debugInfo;
+
+ parserInput.save();
+
+ if (context.dumpLineNumbers) {
+ debugInfo = getDebugInfo(parserInput.i);
+ }
+
+ while (true) {
+ s = this.lessSelector();
+ if (!s) {
+ break;
+ }
+ if (selectors) {
+ selectors.push(s);
+ } else {
+ selectors = [ s ];
+ }
+ parserInput.commentStore.length = 0;
+ if (s.condition && selectors.length > 1) {
+ error("Guards are only currently allowed on a single selector.");
+ }
+ if (! parserInput.$char(',')) { break; }
+ if (s.condition) {
+ error("Guards are only currently allowed on a single selector.");
+ }
+ parserInput.commentStore.length = 0;
+ }
+
+ if (selectors && (rules = this.block())) {
+ parserInput.forget();
+ var ruleset = new(tree.Ruleset)(selectors, rules, context.strictImports);
+ if (context.dumpLineNumbers) {
+ ruleset.debugInfo = debugInfo;
+ }
+ return ruleset;
+ } else {
+ parserInput.restore();
+ }
+ },
+ rule: function (tryAnonymous) {
+ var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable;
+
+ if (c === '.' || c === '#' || c === '&') { return; }
+
+ parserInput.save();
+
+ name = this.variable() || this.ruleProperty();
+ if (name) {
+ isVariable = typeof name === "string";
+
+ if (isVariable) {
+ value = this.detachedRuleset();
+ }
+
+ parserInput.commentStore.length = 0;
+ if (!value) {
+ // a name returned by this.ruleProperty() is always an array of the form:
+ // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
+ // where each item is a tree.Keyword or tree.Variable
+ merge = !isVariable && name.pop().value;
+
+ // prefer to try to parse first if its a variable or we are compressing
+ // but always fallback on the other one
+ var tryValueFirst = !tryAnonymous && (context.compress || isVariable);
+
+ if (tryValueFirst) {
+ value = this.value();
+ }
+ if (!value) {
+ value = this.anonymousValue();
+ if (value) {
+ parserInput.forget();
+ // anonymous values absorb the end ';' which is reequired for them to work
+ return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo);
+ }
+ }
+ if (!tryValueFirst && !value) {
+ value = this.value();
+ }
+
+ important = this.important();
+ }
+
+ if (value && this.end()) {
+ parserInput.forget();
+ return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo);
+ } else {
+ parserInput.restore();
+ if (value && !tryAnonymous) {
+ return this.rule(true);
+ }
+ }
+ } else {
+ parserInput.forget();
+ }
+ },
+ anonymousValue: function () {
+ var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/);
+ if (match) {
+ return new(tree.Anonymous)(match[1]);
+ }
+ },
+
+ //
+ // An @import directive
+ //
+ // @import "lib";
+ //
+ // Depending on our environment, importing is done differently:
+ // In the browser, it's an XHR request, in Node, it would be a
+ // file-system operation. The function used for importing is
+ // stored in `import`, which we pass to the Import constructor.
+ //
+ "import": function () {
+ var path, features, index = parserInput.i;
+
+ var dir = parserInput.$re(/^@import?\s+/);
+
+ if (dir) {
+ var options = (dir ? this.importOptions() : null) || {};
+
+ if ((path = this.entities.quoted() || this.entities.url())) {
+ features = this.mediaFeatures();
+
+ if (!parserInput.$(';')) {
+ parserInput.i = index;
+ error("missing semi-colon or unrecognised media features on import");
+ }
+ features = features && new(tree.Value)(features);
+ return new(tree.Import)(path, features, options, index, fileInfo);
+ }
+ else
+ {
+ parserInput.i = index;
+ error("malformed import statement");
+ }
+ }
+ },
+
+ importOptions: function() {
+ var o, options = {}, optionName, value;
+
+ // list of options, surrounded by parens
+ if (! parserInput.$char('(')) { return null; }
+ do {
+ o = this.importOption();
+ if (o) {
+ optionName = o;
+ value = true;
+ switch(optionName) {
+ case "css":
+ optionName = "less";
+ value = false;
+ break;
+ case "once":
+ optionName = "multiple";
+ value = false;
+ break;
+ }
+ options[optionName] = value;
+ if (! parserInput.$char(',')) { break; }
+ }
+ } while (o);
+ expectChar(')');
+ return options;
+ },
+
+ importOption: function() {
+ var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
+ if (opt) {
+ return opt[1];
+ }
+ },
+
+ mediaFeature: function () {
+ var entities = this.entities, nodes = [], e, p;
+ parserInput.save();
+ do {
+ e = entities.keyword() || entities.variable();
+ if (e) {
+ nodes.push(e);
+ } else if (parserInput.$char('(')) {
+ p = this.property();
+ e = this.value();
+ if (parserInput.$char(')')) {
+ if (p && e) {
+ nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true)));
+ } else if (e) {
+ nodes.push(new(tree.Paren)(e));
+ } else {
+ parserInput.restore("badly formed media feature definition");
+ return null;
+ }
+ } else {
+ parserInput.restore("Missing closing ')'");
+ return null;
+ }
+ }
+ } while (e);
+
+ parserInput.forget();
+ if (nodes.length > 0) {
+ return new(tree.Expression)(nodes);
+ }
+ },
+
+ mediaFeatures: function () {
+ var entities = this.entities, features = [], e;
+ do {
+ e = this.mediaFeature();
+ if (e) {
+ features.push(e);
+ if (! parserInput.$char(',')) { break; }
+ } else {
+ e = entities.variable();
+ if (e) {
+ features.push(e);
+ if (! parserInput.$char(',')) { break; }
+ }
+ }
+ } while (e);
+
+ return features.length > 0 ? features : null;
+ },
+
+ media: function () {
+ var features, rules, media, debugInfo;
+
+ if (context.dumpLineNumbers) {
+ debugInfo = getDebugInfo(parserInput.i);
+ }
+
+ if (parserInput.$re(/^@media/)) {
+ features = this.mediaFeatures();
+
+ rules = this.block();
+ if (rules) {
+ media = new(tree.Media)(rules, features, parserInput.i, fileInfo);
+ if (context.dumpLineNumbers) {
+ media.debugInfo = debugInfo;
+ }
+ return media;
+ }
+ }
+ },
+
+ //
+ // A CSS Directive
+ //
+ // @charset "utf-8";
+ //
+ directive: function () {
+ var index = parserInput.i, name, value, rules, nonVendorSpecificName,
+ hasIdentifier, hasExpression, hasUnknown, hasBlock = true;
+
+ if (parserInput.currentChar() !== '@') { return; }
+
+ value = this['import']() || this.media();
+ if (value) {
+ return value;
+ }
+
+ parserInput.save();
+
+ name = parserInput.$re(/^@[a-z-]+/);
+
+ if (!name) { return; }
+
+ nonVendorSpecificName = name;
+ if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
+ nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
+ }
+
+ switch(nonVendorSpecificName) {
+ /*
+ case "@font-face":
+ case "@viewport":
+ case "@top-left":
+ case "@top-left-corner":
+ case "@top-center":
+ case "@top-right":
+ case "@top-right-corner":
+ case "@bottom-left":
+ case "@bottom-left-corner":
+ case "@bottom-center":
+ case "@bottom-right":
+ case "@bottom-right-corner":
+ case "@left-top":
+ case "@left-middle":
+ case "@left-bottom":
+ case "@right-top":
+ case "@right-middle":
+ case "@right-bottom":
+ hasBlock = true;
+ break;
+ */
+ case "@counter-style":
+ hasIdentifier = true;
+ hasBlock = true;
+ break;
+ case "@charset":
+ hasIdentifier = true;
+ hasBlock = false;
+ break;
+ case "@namespace":
+ hasExpression = true;
+ hasBlock = false;
+ break;
+ case "@keyframes":
+ hasIdentifier = true;
+ break;
+ case "@host":
+ case "@page":
+ case "@document":
+ case "@supports":
+ hasUnknown = true;
+ break;
+ }
+
+ parserInput.commentStore.length = 0;
+
+ if (hasIdentifier) {
+ value = this.entity();
+ if (!value) {
+ error("expected " + name + " identifier");
+ }
+ } else if (hasExpression) {
+ value = this.expression();
+ if (!value) {
+ error("expected " + name + " expression");
+ }
+ } else if (hasUnknown) {
+ value = (parserInput.$re(/^[^{;]+/) || '').trim();
+ if (value) {
+ value = new(tree.Anonymous)(value);
+ }
+ }
+
+ if (hasBlock) {
+ rules = this.blockRuleset();
+ }
+
+ if (rules || (!hasBlock && value && parserInput.$char(';'))) {
+ parserInput.forget();
+ return new(tree.Directive)(name, value, rules, index, fileInfo,
+ context.dumpLineNumbers ? getDebugInfo(index) : null);
+ }
+
+ parserInput.restore("directive options not recognised");
+ },
+
+ //
+ // A Value is a comma-delimited list of Expressions
+ //
+ // font-family: Baskerville, Georgia, serif;
+ //
+ // In a Rule, a Value represents everything after the `:`,
+ // and before the `;`.
+ //
+ value: function () {
+ var e, expressions = [];
+
+ do {
+ e = this.expression();
+ if (e) {
+ expressions.push(e);
+ if (! parserInput.$char(',')) { break; }
+ }
+ } while (e);
+
+ if (expressions.length > 0) {
+ return new(tree.Value)(expressions);
+ }
+ },
+ important: function () {
+ if (parserInput.currentChar() === '!') {
+ return parserInput.$re(/^! *important/);
+ }
+ },
+ sub: function () {
+ var a, e;
+
+ parserInput.save();
+ if (parserInput.$char('(')) {
+ a = this.addition();
+ if (a && parserInput.$char(')')) {
+ parserInput.forget();
+ e = new(tree.Expression)([a]);
+ e.parens = true;
+ return e;
+ }
+ parserInput.restore("Expected ')'");
+ return;
+ }
+ parserInput.restore();
+ },
+ multiplication: function () {
+ var m, a, op, operation, isSpaced;
+ m = this.operand();
+ if (m) {
+ isSpaced = parserInput.isWhitespace(-1);
+ while (true) {
+ if (parserInput.peek(/^\/[*\/]/)) {
+ break;
+ }
+
+ parserInput.save();
+
+ op = parserInput.$char('/') || parserInput.$char('*');
+
+ if (!op) { parserInput.forget(); break; }
+
+ a = this.operand();
+
+ if (!a) { parserInput.restore(); break; }
+ parserInput.forget();
+
+ m.parensInOp = true;
+ a.parensInOp = true;
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
+ isSpaced = parserInput.isWhitespace(-1);
+ }
+ return operation || m;
+ }
+ },
+ addition: function () {
+ var m, a, op, operation, isSpaced;
+ m = this.multiplication();
+ if (m) {
+ isSpaced = parserInput.isWhitespace(-1);
+ while (true) {
+ op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-')));
+ if (!op) {
+ break;
+ }
+ a = this.multiplication();
+ if (!a) {
+ break;
+ }
+
+ m.parensInOp = true;
+ a.parensInOp = true;
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
+ isSpaced = parserInput.isWhitespace(-1);
+ }
+ return operation || m;
+ }
+ },
+ conditions: function () {
+ var a, b, index = parserInput.i, condition;
+
+ a = this.condition();
+ if (a) {
+ while (true) {
+ if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) {
+ break;
+ }
+ b = this.condition();
+ if (!b) {
+ break;
+ }
+ condition = new(tree.Condition)('or', condition || a, b, index);
+ }
+ return condition || a;
+ }
+ },
+ condition: function () {
+ var entities = this.entities, index = parserInput.i, negate = false,
+ a, b, c, op;
+
+ if (parserInput.$re(/^not/)) { negate = true; }
+ expectChar('(');
+ a = this.addition() || entities.keyword() || entities.quoted();
+ if (a) {
+ op = parserInput.$re(/^(?:>=|<=|=<|[<=>])/);
+ if (op) {
+ b = this.addition() || entities.keyword() || entities.quoted();
+ if (b) {
+ c = new(tree.Condition)(op, a, b, index, negate);
+ } else {
+ error('expected expression');
+ }
+ } else {
+ c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
+ }
+ expectChar(')');
+ return parserInput.$re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;
+ }
+ },
+
+ //
+ // An operand is anything that can be part of an operation,
+ // such as a Color, or a Variable
+ //
+ operand: function () {
+ var entities = this.entities, negate;
+
+ if (parserInput.peek(/^-[@\(]/)) {
+ negate = parserInput.$char('-');
+ }
+
+ var o = this.sub() || entities.dimension() ||
+ entities.color() || entities.variable() ||
+ entities.call();
+
+ if (negate) {
+ o.parensInOp = true;
+ o = new(tree.Negative)(o);
+ }
+
+ return o;
+ },
+
+ //
+ // Expressions either represent mathematical operations,
+ // or white-space delimited Entities.
+ //
+ // 1px solid black
+ // @var * 2
+ //
+ expression: function () {
+ var entities = [], e, delim;
+
+ do {
+ e = this.comment();
+ if (e) {
+ entities.push(e);
+ continue;
+ }
+ e = this.addition() || this.entity();
+ if (e) {
+ entities.push(e);
+ // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
+ if (!parserInput.peek(/^\/[\/*]/)) {
+ delim = parserInput.$char('/');
+ if (delim) {
+ entities.push(new(tree.Anonymous)(delim));
+ }
+ }
+ }
+ } while (e);
+ if (entities.length > 0) {
+ return new(tree.Expression)(entities);
+ }
+ },
+ property: function () {
+ var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
+ if (name) {
+ return name[1];
+ }
+ },
+ ruleProperty: function () {
+ var name = [], index = [], s, k;
+
+ parserInput.save();
+
+ function match(re) {
+ var i = parserInput.i,
+ chunk = parserInput.$re(re);
+ if (chunk) {
+ index.push(i);
+ return name.push(chunk[1]);
+ }
+ }
+
+ match(/^(\*?)/);
+ while (true) {
+ if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) {
+ break;
+ }
+ }
+
+ if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) {
+ parserInput.forget();
+
+ // at last, we have the complete match now. move forward,
+ // convert name particles to tree objects and return:
+ if (name[0] === '') {
+ name.shift();
+ index.shift();
+ }
+ for (k = 0; k < name.length; k++) {
+ s = name[k];
+ name[k] = (s.charAt(0) !== '@') ?
+ new(tree.Keyword)(s) :
+ new(tree.Variable)('@' + s.slice(2, -1),
+ index[k], fileInfo);
+ }
+ return name;
+ }
+ parserInput.restore();
+ }
+ }
+ };
+};
+Parser.serializeVars = function(vars) {
+ var s = '';
+
+ for (var name in vars) {
+ if (Object.hasOwnProperty.call(vars, name)) {
+ var value = vars[name];
+ s += ((name[0] === '@') ? '' : '@') + name + ': ' + value +
+ ((String(value).slice(-1) === ';') ? '' : ';');
+ }
+ }
+
+ return s;
+};
+
+module.exports = Parser;
+
+},{"../less-error":30,"../tree":59,"../utils":80,"../visitors":84,"./parser-input":35}],37:[function(require,module,exports){
+/**
+ * Plugin Manager
+ */
+var PluginManager = function(less) {
+ this.less = less;
+ this.visitors = [];
+ this.preProcessors = [];
+ this.postProcessors = [];
+ this.installedPlugins = [];
+ this.fileManagers = [];
+};
+/**
+ * Adds all the plugins in the array
+ * @param {Array} plugins
+ */
+PluginManager.prototype.addPlugins = function(plugins) {
+ if (plugins) {
+ for (var i = 0; i < plugins.length; i++) {
+ this.addPlugin(plugins[i]);
+ }
+ }
+};
+/**
+ *
+ * @param plugin
+ */
+PluginManager.prototype.addPlugin = function(plugin) {
+ this.installedPlugins.push(plugin);
+ plugin.install(this.less, this);
+};
+/**
+ * Adds a visitor. The visitor object has options on itself to determine
+ * when it should run.
+ * @param visitor
+ */
+PluginManager.prototype.addVisitor = function(visitor) {
+ this.visitors.push(visitor);
+};
+/**
+ * Adds a pre processor object
+ * @param {object} preProcessor
+ * @param {number} priority - guidelines 1 = before import, 1000 = import, 2000 = after import
+ */
+PluginManager.prototype.addPreProcessor = function(preProcessor, priority) {
+ var indexToInsertAt;
+ for (indexToInsertAt = 0; indexToInsertAt < this.preProcessors.length; indexToInsertAt++) {
+ if (this.preProcessors[indexToInsertAt].priority >= priority) {
+ break;
+ }
+ }
+ this.preProcessors.splice(indexToInsertAt, 0, {preProcessor: preProcessor, priority: priority});
+};
+/**
+ * Adds a post processor object
+ * @param {object} postProcessor
+ * @param {number} priority - guidelines 1 = before compression, 1000 = compression, 2000 = after compression
+ */
+PluginManager.prototype.addPostProcessor = function(postProcessor, priority) {
+ var indexToInsertAt;
+ for (indexToInsertAt = 0; indexToInsertAt < this.postProcessors.length; indexToInsertAt++) {
+ if (this.postProcessors[indexToInsertAt].priority >= priority) {
+ break;
+ }
+ }
+ this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority});
+};
+/**
+ *
+ * @param manager
+ */
+PluginManager.prototype.addFileManager = function(manager) {
+ this.fileManagers.push(manager);
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getPreProcessors = function() {
+ var preProcessors = [];
+ for (var i = 0; i < this.preProcessors.length; i++) {
+ preProcessors.push(this.preProcessors[i].preProcessor);
+ }
+ return preProcessors;
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getPostProcessors = function() {
+ var postProcessors = [];
+ for (var i = 0; i < this.postProcessors.length; i++) {
+ postProcessors.push(this.postProcessors[i].postProcessor);
+ }
+ return postProcessors;
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getVisitors = function() {
+ return this.visitors;
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getFileManagers = function() {
+ return this.fileManagers;
+};
+module.exports = PluginManager;
+
+},{}],38:[function(require,module,exports){
+var PromiseConstructor;
+
+module.exports = function(environment, ParseTree, ImportManager) {
+ var render = function (input, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ if (!callback) {
+ if (!PromiseConstructor) {
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
+ }
+ var self = this;
+ return new PromiseConstructor(function (resolve, reject) {
+ render.call(self, input, options, function(err, output) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(output);
+ }
+ });
+ });
+ } else {
+ this.parse(input, options, function(err, root, imports, options) {
+ if (err) { return callback(err); }
+
+ var result;
+ try {
+ var parseTree = new ParseTree(root, imports);
+ result = parseTree.toCSS(options);
+ }
+ catch (err) { return callback(err); }
+
+ callback(null, result);
+ });
+ }
+ };
+
+ return render;
+};
+
+},{"promise":undefined}],39:[function(require,module,exports){
+module.exports = function (SourceMapOutput, environment) {
+
+ var SourceMapBuilder = function (options) {
+ this.options = options;
+ };
+
+ SourceMapBuilder.prototype.toCSS = function(rootNode, options, imports) {
+ var sourceMapOutput = new SourceMapOutput(
+ {
+ contentsIgnoredCharsMap: imports.contentsIgnoredChars,
+ rootNode: rootNode,
+ contentsMap: imports.contents,
+ sourceMapFilename: this.options.sourceMapFilename,
+ sourceMapURL: this.options.sourceMapURL,
+ outputFilename: this.options.sourceMapOutputFilename,
+ sourceMapBasepath: this.options.sourceMapBasepath,
+ sourceMapRootpath: this.options.sourceMapRootpath,
+ outputSourceFiles: this.options.outputSourceFiles,
+ sourceMapGenerator: this.options.sourceMapGenerator,
+ sourceMapFileInline: this.options.sourceMapFileInline
+ });
+
+ var css = sourceMapOutput.toCSS(options);
+ this.sourceMap = sourceMapOutput.sourceMap;
+ this.sourceMapURL = sourceMapOutput.sourceMapURL;
+ if (this.options.sourceMapInputFilename) {
+ this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename);
+ }
+ return css + this.getCSSAppendage();
+ };
+
+ SourceMapBuilder.prototype.getCSSAppendage = function() {
+
+ var sourceMapURL = this.sourceMapURL;
+ if (this.options.sourceMapFileInline) {
+ if (this.sourceMap === undefined) {
+ return "";
+ }
+ sourceMapURL = "data:application/json;base64," + environment.encodeBase64(this.sourceMap);
+ }
+
+ if (sourceMapURL) {
+ return "/*# sourceMappingURL=" + sourceMapURL + " */";
+ }
+ return "";
+ };
+
+ SourceMapBuilder.prototype.getExternalSourceMap = function() {
+ return this.sourceMap;
+ };
+ SourceMapBuilder.prototype.setExternalSourceMap = function(sourceMap) {
+ this.sourceMap = sourceMap;
+ };
+
+ SourceMapBuilder.prototype.isInline = function() {
+ return this.options.sourceMapFileInline;
+ };
+ SourceMapBuilder.prototype.getSourceMapURL = function() {
+ return this.sourceMapURL;
+ };
+ SourceMapBuilder.prototype.getOutputFilename = function() {
+ return this.options.sourceMapOutputFilename;
+ };
+ SourceMapBuilder.prototype.getInputFilename = function() {
+ return this.sourceMapInputFilename;
+ };
+
+ return SourceMapBuilder;
+};
+
+},{}],40:[function(require,module,exports){
+module.exports = function (environment) {
+
+ var SourceMapOutput = function (options) {
+ this._css = [];
+ this._rootNode = options.rootNode;
+ this._contentsMap = options.contentsMap;
+ this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap;
+ if (options.sourceMapFilename) {
+ this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/');
+ }
+ this._outputFilename = options.outputFilename;
+ this.sourceMapURL = options.sourceMapURL;
+ if (options.sourceMapBasepath) {
+ this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
+ }
+ if (options.sourceMapRootpath) {
+ this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/');
+ if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') {
+ this._sourceMapRootpath += '/';
+ }
+ } else {
+ this._sourceMapRootpath = "";
+ }
+ this._outputSourceFiles = options.outputSourceFiles;
+ this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator();
+
+ this._lineNumber = 0;
+ this._column = 0;
+ };
+
+ SourceMapOutput.prototype.normalizeFilename = function(filename) {
+ filename = filename.replace(/\\/g, '/');
+
+ if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) {
+ filename = filename.substring(this._sourceMapBasepath.length);
+ if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') {
+ filename = filename.substring(1);
+ }
+ }
+ return (this._sourceMapRootpath || "") + filename;
+ };
+
+ SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
+
+ //ignore adding empty strings
+ if (!chunk) {
+ return;
+ }
+
+ var lines,
+ sourceLines,
+ columns,
+ sourceColumns,
+ i;
+
+ if (fileInfo) {
+ var inputSource = this._contentsMap[fileInfo.filename];
+
+ // remove vars/banner added to the top of the file
+ if (this._contentsIgnoredCharsMap[fileInfo.filename]) {
+ // adjust the index
+ index -= this._contentsIgnoredCharsMap[fileInfo.filename];
+ if (index < 0) { index = 0; }
+ // adjust the source
+ inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]);
+ }
+ inputSource = inputSource.substring(0, index);
+ sourceLines = inputSource.split("\n");
+ sourceColumns = sourceLines[sourceLines.length - 1];
+ }
+
+ lines = chunk.split("\n");
+ columns = lines[lines.length - 1];
+
+ if (fileInfo) {
+ if (!mapLines) {
+ this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column},
+ original: { line: sourceLines.length, column: sourceColumns.length},
+ source: this.normalizeFilename(fileInfo.filename)});
+ } else {
+ for (i = 0; i < lines.length; i++) {
+ this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0},
+ original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0},
+ source: this.normalizeFilename(fileInfo.filename)});
+ }
+ }
+ }
+
+ if (lines.length === 1) {
+ this._column += columns.length;
+ } else {
+ this._lineNumber += lines.length - 1;
+ this._column = columns.length;
+ }
+
+ this._css.push(chunk);
+ };
+
+ SourceMapOutput.prototype.isEmpty = function() {
+ return this._css.length === 0;
+ };
+
+ SourceMapOutput.prototype.toCSS = function(context) {
+ this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
+
+ if (this._outputSourceFiles) {
+ for (var filename in this._contentsMap) {
+ if (this._contentsMap.hasOwnProperty(filename))
+ {
+ var source = this._contentsMap[filename];
+ if (this._contentsIgnoredCharsMap[filename]) {
+ source = source.slice(this._contentsIgnoredCharsMap[filename]);
+ }
+ this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
+ }
+ }
+ }
+
+ this._rootNode.genCSS(context, this);
+
+ if (this._css.length > 0) {
+ var sourceMapURL,
+ sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
+
+ if (this.sourceMapURL) {
+ sourceMapURL = this.sourceMapURL;
+ } else if (this._sourceMapFilename) {
+ sourceMapURL = this._sourceMapFilename;
+ }
+ this.sourceMapURL = sourceMapURL;
+
+ this.sourceMap = sourceMapContent;
+ }
+
+ return this._css.join('');
+ };
+
+ return SourceMapOutput;
+};
+
+},{}],41:[function(require,module,exports){
+var contexts = require("./contexts"),
+ visitor = require("./visitors"),
+ tree = require("./tree");
+
+module.exports = function(root, options) {
+ options = options || {};
+ var evaldRoot,
+ variables = options.variables,
+ evalEnv = new contexts.Eval(options);
+
+ //
+ // Allows setting variables with a hash, so:
+ //
+ // `{ color: new tree.Color('#f01') }` will become:
+ //
+ // new tree.Rule('@color',
+ // new tree.Value([
+ // new tree.Expression([
+ // new tree.Color('#f01')
+ // ])
+ // ])
+ // )
+ //
+ if (typeof variables === 'object' && !Array.isArray(variables)) {
+ variables = Object.keys(variables).map(function (k) {
+ var value = variables[k];
+
+ if (! (value instanceof tree.Value)) {
+ if (! (value instanceof tree.Expression)) {
+ value = new tree.Expression([value]);
+ }
+ value = new tree.Value([value]);
+ }
+ return new tree.Rule('@' + k, value, false, null, 0);
+ });
+ evalEnv.frames = [new tree.Ruleset(null, variables)];
+ }
+
+ var preEvalVisitors = [],
+ visitors = [
+ new visitor.JoinSelectorVisitor(),
+ new visitor.ExtendVisitor(),
+ new visitor.ToCSSVisitor({compress: Boolean(options.compress)})
+ ], i;
+
+ if (options.pluginManager) {
+ var pluginVisitors = options.pluginManager.getVisitors();
+ for (i = 0; i < pluginVisitors.length; i++) {
+ var pluginVisitor = pluginVisitors[i];
+ if (pluginVisitor.isPreEvalVisitor) {
+ preEvalVisitors.push(pluginVisitor);
+ } else {
+ if (pluginVisitor.isPreVisitor) {
+ visitors.splice(0, 0, pluginVisitor);
+ } else {
+ visitors.push(pluginVisitor);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < preEvalVisitors.length; i++) {
+ preEvalVisitors[i].run(root);
+ }
+
+ evaldRoot = root.eval(evalEnv);
+
+ for (i = 0; i < visitors.length; i++) {
+ visitors[i].run(evaldRoot);
+ }
+
+ return evaldRoot;
+};
+
+},{"./contexts":10,"./tree":59,"./visitors":84}],42:[function(require,module,exports){
+var Node = require("./node");
+
+var Alpha = function (val) {
+ this.value = val;
+};
+Alpha.prototype = new Node();
+Alpha.prototype.type = "Alpha";
+
+Alpha.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+Alpha.prototype.eval = function (context) {
+ if (this.value.eval) { return new Alpha(this.value.eval(context)); }
+ return this;
+};
+Alpha.prototype.genCSS = function (context, output) {
+ output.add("alpha(opacity=");
+
+ if (this.value.genCSS) {
+ this.value.genCSS(context, output);
+ } else {
+ output.add(this.value);
+ }
+
+ output.add(")");
+};
+
+module.exports = Alpha;
+
+},{"./node":67}],43:[function(require,module,exports){
+var Node = require("./node");
+
+var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) {
+ this.value = value;
+ this.index = index;
+ this.mapLines = mapLines;
+ this.currentFileInfo = currentFileInfo;
+ this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
+};
+Anonymous.prototype = new Node();
+Anonymous.prototype.type = "Anonymous";
+Anonymous.prototype.eval = function () {
+ return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
+};
+Anonymous.prototype.compare = function (other) {
+ return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
+};
+Anonymous.prototype.isRulesetLike = function() {
+ return this.rulesetLike;
+};
+Anonymous.prototype.genCSS = function (context, output) {
+ output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
+};
+module.exports = Anonymous;
+
+},{"./node":67}],44:[function(require,module,exports){
+var Node = require("./node");
+
+var Assignment = function (key, val) {
+ this.key = key;
+ this.value = val;
+};
+
+Assignment.prototype = new Node();
+Assignment.prototype.type = "Assignment";
+Assignment.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+Assignment.prototype.eval = function (context) {
+ if (this.value.eval) {
+ return new Assignment(this.key, this.value.eval(context));
+ }
+ return this;
+};
+Assignment.prototype.genCSS = function (context, output) {
+ output.add(this.key + '=');
+ if (this.value.genCSS) {
+ this.value.genCSS(context, output);
+ } else {
+ output.add(this.value);
+ }
+};
+module.exports = Assignment;
+
+},{"./node":67}],45:[function(require,module,exports){
+var Node = require("./node");
+
+var Attribute = function (key, op, value) {
+ this.key = key;
+ this.op = op;
+ this.value = value;
+};
+Attribute.prototype = new Node();
+Attribute.prototype.type = "Attribute";
+Attribute.prototype.eval = function (context) {
+ return new Attribute(this.key.eval ? this.key.eval(context) : this.key,
+ this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value);
+};
+Attribute.prototype.genCSS = function (context, output) {
+ output.add(this.toCSS(context));
+};
+Attribute.prototype.toCSS = function (context) {
+ var value = this.key.toCSS ? this.key.toCSS(context) : this.key;
+
+ if (this.op) {
+ value += this.op;
+ value += (this.value.toCSS ? this.value.toCSS(context) : this.value);
+ }
+
+ return '[' + value + ']';
+};
+module.exports = Attribute;
+
+},{"./node":67}],46:[function(require,module,exports){
+var Node = require("./node"),
+ FunctionCaller = require("../functions/function-caller");
+//
+// A function call node.
+//
+var Call = function (name, args, index, currentFileInfo) {
+ this.name = name;
+ this.args = args;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+Call.prototype = new Node();
+Call.prototype.type = "Call";
+Call.prototype.accept = function (visitor) {
+ if (this.args) {
+ this.args = visitor.visitArray(this.args);
+ }
+};
+//
+// When evaluating a function call,
+// we either find the function in the functionRegistry,
+// in which case we call it, passing the evaluated arguments,
+// if this returns null or we cannot find the function, we
+// simply print it out as it appeared originally [2].
+//
+// The reason why we evaluate the arguments, is in the case where
+// we try to pass a variable to a function, like: `saturate(@color)`.
+// The function should receive the value, not the variable.
+//
+Call.prototype.eval = function (context) {
+ var args = this.args.map(function (a) { return a.eval(context); }),
+ result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo);
+
+ if (funcCaller.isValid()) { // 1.
+ try {
+ result = funcCaller.call(args);
+ if (result != null) {
+ return result;
+ }
+ } catch (e) {
+ throw { type: e.type || "Runtime",
+ message: "error evaluating function `" + this.name + "`" +
+ (e.message ? ': ' + e.message : ''),
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+ }
+
+ return new Call(this.name, args, this.index, this.currentFileInfo);
+};
+Call.prototype.genCSS = function (context, output) {
+ output.add(this.name + "(", this.currentFileInfo, this.index);
+
+ for (var i = 0; i < this.args.length; i++) {
+ this.args[i].genCSS(context, output);
+ if (i + 1 < this.args.length) {
+ output.add(", ");
+ }
+ }
+
+ output.add(")");
+};
+module.exports = Call;
+
+},{"../functions/function-caller":20,"./node":67}],47:[function(require,module,exports){
+var Node = require("./node"),
+ colors = require("../data/colors");
+
+//
+// RGB Colors - #ff0014, #eee
+//
+var Color = function (rgb, a) {
+ //
+ // The end goal here, is to parse the arguments
+ // into an integer triplet, such as `128, 255, 0`
+ //
+ // This facilitates operations and conversions.
+ //
+ if (Array.isArray(rgb)) {
+ this.rgb = rgb;
+ } else if (rgb.length == 6) {
+ this.rgb = rgb.match(/.{2}/g).map(function (c) {
+ return parseInt(c, 16);
+ });
+ } else {
+ this.rgb = rgb.split('').map(function (c) {
+ return parseInt(c + c, 16);
+ });
+ }
+ this.alpha = typeof a === 'number' ? a : 1;
+};
+
+Color.prototype = new Node();
+Color.prototype.type = "Color";
+
+function clamp(v, max) {
+ return Math.min(Math.max(v, 0), max);
+}
+
+function toHex(v) {
+ return '#' + v.map(function (c) {
+ c = clamp(Math.round(c), 255);
+ return (c < 16 ? '0' : '') + c.toString(16);
+ }).join('');
+}
+
+Color.prototype.luma = function () {
+ var r = this.rgb[0] / 255,
+ g = this.rgb[1] / 255,
+ b = this.rgb[2] / 255;
+
+ r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
+ g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
+ b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
+
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+};
+Color.prototype.genCSS = function (context, output) {
+ output.add(this.toCSS(context));
+};
+Color.prototype.toCSS = function (context, doNotCompress) {
+ var compress = context && context.compress && !doNotCompress, color, alpha;
+
+ // `value` is set if this color was originally
+ // converted from a named color string so we need
+ // to respect this and try to output named color too.
+ if (this.value) {
+ return this.value;
+ }
+
+ // If we have some transparency, the only way to represent it
+ // is via `rgba`. Otherwise, we use the hex representation,
+ // which has better compatibility with older browsers.
+ // Values are capped between `0` and `255`, rounded and zero-padded.
+ alpha = this.fround(context, this.alpha);
+ if (alpha < 1) {
+ return "rgba(" + this.rgb.map(function (c) {
+ return clamp(Math.round(c), 255);
+ }).concat(clamp(alpha, 1))
+ .join(',' + (compress ? '' : ' ')) + ")";
+ }
+
+ color = this.toRGB();
+
+ if (compress) {
+ var splitcolor = color.split('');
+
+ // Convert color to short format
+ if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
+ color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
+ }
+ }
+
+ return color;
+};
+
+//
+// Operations have to be done per-channel, if not,
+// channels will spill onto each other. Once we have
+// our result, in the form of an integer triplet,
+// we create a new Color node to hold the result.
+//
+Color.prototype.operate = function (context, op, other) {
+ var rgb = [];
+ var alpha = this.alpha * (1 - other.alpha) + other.alpha;
+ for (var c = 0; c < 3; c++) {
+ rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]);
+ }
+ return new Color(rgb, alpha);
+};
+Color.prototype.toRGB = function () {
+ return toHex(this.rgb);
+};
+Color.prototype.toHSL = function () {
+ var r = this.rgb[0] / 255,
+ g = this.rgb[1] / 255,
+ b = this.rgb[2] / 255,
+ a = this.alpha;
+
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2, d = max - min;
+
+ if (max === min) {
+ h = s = 0;
+ } else {
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+
+ switch (max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+ return { h: h * 360, s: s, l: l, a: a };
+};
+//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+Color.prototype.toHSV = function () {
+ var r = this.rgb[0] / 255,
+ g = this.rgb[1] / 255,
+ b = this.rgb[2] / 255,
+ a = this.alpha;
+
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, v = max;
+
+ var d = max - min;
+ if (max === 0) {
+ s = 0;
+ } else {
+ s = d / max;
+ }
+
+ if (max === min) {
+ h = 0;
+ } else {
+ switch(max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+ return { h: h * 360, s: s, v: v, a: a };
+};
+Color.prototype.toARGB = function () {
+ return toHex([this.alpha * 255].concat(this.rgb));
+};
+Color.prototype.compare = function (x) {
+ return (x.rgb &&
+ x.rgb[0] === this.rgb[0] &&
+ x.rgb[1] === this.rgb[1] &&
+ x.rgb[2] === this.rgb[2] &&
+ x.alpha === this.alpha) ? 0 : undefined;
+};
+
+Color.fromKeyword = function(keyword) {
+ var c, key = keyword.toLowerCase();
+ if (colors.hasOwnProperty(key)) {
+ c = new Color(colors[key].slice(1));
+ }
+ else if (key === "transparent") {
+ c = new Color([0, 0, 0], 0);
+ }
+
+ if (c) {
+ c.value = keyword;
+ return c;
+ }
+};
+module.exports = Color;
+
+},{"../data/colors":11,"./node":67}],48:[function(require,module,exports){
+var Node = require("./node");
+
+var Combinator = function (value) {
+ if (value === ' ') {
+ this.value = ' ';
+ this.emptyOrWhitespace = true;
+ } else {
+ this.value = value ? value.trim() : "";
+ this.emptyOrWhitespace = this.value === "";
+ }
+};
+Combinator.prototype = new Node();
+Combinator.prototype.type = "Combinator";
+var _noSpaceCombinators = {
+ '': true,
+ ' ': true,
+ '|': true
+};
+Combinator.prototype.genCSS = function (context, output) {
+ var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
+ output.add(spaceOrEmpty + this.value + spaceOrEmpty);
+};
+module.exports = Combinator;
+
+},{"./node":67}],49:[function(require,module,exports){
+var Node = require("./node"),
+ getDebugInfo = require("./debug-info");
+
+var Comment = function (value, isLineComment, index, currentFileInfo) {
+ this.value = value;
+ this.isLineComment = isLineComment;
+ this.currentFileInfo = currentFileInfo;
+};
+Comment.prototype = new Node();
+Comment.prototype.type = "Comment";
+Comment.prototype.genCSS = function (context, output) {
+ if (this.debugInfo) {
+ output.add(getDebugInfo(context, this), this.currentFileInfo, this.index);
+ }
+ output.add(this.value);
+};
+Comment.prototype.isSilent = function(context) {
+ var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
+ isCompressed = context.compress && this.value[2] !== "!";
+ return this.isLineComment || isReference || isCompressed;
+};
+Comment.prototype.markReferenced = function () {
+ this.isReferenced = true;
+};
+Comment.prototype.isRulesetLike = function(root) {
+ return Boolean(root);
+};
+module.exports = Comment;
+
+},{"./debug-info":51,"./node":67}],50:[function(require,module,exports){
+var Node = require("./node");
+
+var Condition = function (op, l, r, i, negate) {
+ this.op = op.trim();
+ this.lvalue = l;
+ this.rvalue = r;
+ this.index = i;
+ this.negate = negate;
+};
+Condition.prototype = new Node();
+Condition.prototype.type = "Condition";
+Condition.prototype.accept = function (visitor) {
+ this.lvalue = visitor.visit(this.lvalue);
+ this.rvalue = visitor.visit(this.rvalue);
+};
+Condition.prototype.eval = function (context) {
+ var result = (function (op, a, b) {
+ switch (op) {
+ case 'and': return a && b;
+ case 'or': return a || b;
+ default:
+ switch (Node.compare(a, b)) {
+ case -1:
+ return op === '<' || op === '=<' || op === '<=';
+ case 0:
+ return op === '=' || op === '>=' || op === '=<' || op === '<=';
+ case 1:
+ return op === '>' || op === '>=';
+ default:
+ return false;
+ }
+ }
+ })(this.op, this.lvalue.eval(context), this.rvalue.eval(context));
+
+ return this.negate ? !result : result;
+};
+module.exports = Condition;
+
+},{"./node":67}],51:[function(require,module,exports){
+var debugInfo = function(context, ctx, lineSeparator) {
+ var result = "";
+ if (context.dumpLineNumbers && !context.compress) {
+ switch(context.dumpLineNumbers) {
+ case 'comments':
+ result = debugInfo.asComment(ctx);
+ break;
+ case 'mediaquery':
+ result = debugInfo.asMediaQuery(ctx);
+ break;
+ case 'all':
+ result = debugInfo.asComment(ctx) + (lineSeparator || "") + debugInfo.asMediaQuery(ctx);
+ break;
+ }
+ }
+ return result;
+};
+
+debugInfo.asComment = function(ctx) {
+ return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
+};
+
+debugInfo.asMediaQuery = function(ctx) {
+ var filenameWithProtocol = ctx.debugInfo.fileName;
+ if (!/^[a-z]+:\/\//i.test(filenameWithProtocol)) {
+ filenameWithProtocol = 'file://' + filenameWithProtocol;
+ }
+ return '@media -sass-debug-info{filename{font-family:' +
+ filenameWithProtocol.replace(/([.:\/\\])/g, function (a) {
+ if (a == '\\') {
+ a = '\/';
+ }
+ return '\\' + a;
+ }) +
+ '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
+};
+
+module.exports = debugInfo;
+
+},{}],52:[function(require,module,exports){
+var Node = require("./node"),
+ contexts = require("../contexts");
+
+var DetachedRuleset = function (ruleset, frames) {
+ this.ruleset = ruleset;
+ this.frames = frames;
+};
+DetachedRuleset.prototype = new Node();
+DetachedRuleset.prototype.type = "DetachedRuleset";
+DetachedRuleset.prototype.evalFirst = true;
+DetachedRuleset.prototype.accept = function (visitor) {
+ this.ruleset = visitor.visit(this.ruleset);
+};
+DetachedRuleset.prototype.eval = function (context) {
+ var frames = this.frames || context.frames.slice(0);
+ return new DetachedRuleset(this.ruleset, frames);
+};
+DetachedRuleset.prototype.callEval = function (context) {
+ return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context);
+};
+module.exports = DetachedRuleset;
+
+},{"../contexts":10,"./node":67}],53:[function(require,module,exports){
+var Node = require("./node"),
+ unitConversions = require("../data/unit-conversions"),
+ Unit = require("./unit"),
+ Color = require("./color");
+
+//
+// A number with a unit
+//
+var Dimension = function (value, unit) {
+ this.value = parseFloat(value);
+ this.unit = (unit && unit instanceof Unit) ? unit :
+ new Unit(unit ? [unit] : undefined);
+};
+
+Dimension.prototype = new Node();
+Dimension.prototype.type = "Dimension";
+Dimension.prototype.accept = function (visitor) {
+ this.unit = visitor.visit(this.unit);
+};
+Dimension.prototype.eval = function (context) {
+ return this;
+};
+Dimension.prototype.toColor = function () {
+ return new Color([this.value, this.value, this.value]);
+};
+Dimension.prototype.genCSS = function (context, output) {
+ if ((context && context.strictUnits) && !this.unit.isSingular()) {
+ throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: " + this.unit.toString());
+ }
+
+ var value = this.fround(context, this.value),
+ strValue = String(value);
+
+ if (value !== 0 && value < 0.000001 && value > -0.000001) {
+ // would be output 1e-6 etc.
+ strValue = value.toFixed(20).replace(/0+$/, "");
+ }
+
+ if (context && context.compress) {
+ // Zero values doesn't need a unit
+ if (value === 0 && this.unit.isLength()) {
+ output.add(strValue);
+ return;
+ }
+
+ // Float values doesn't need a leading zero
+ if (value > 0 && value < 1) {
+ strValue = (strValue).substr(1);
+ }
+ }
+
+ output.add(strValue);
+ this.unit.genCSS(context, output);
+};
+
+// In an operation between two Dimensions,
+// we default to the first Dimension's unit,
+// so `1px + 2` will yield `3px`.
+Dimension.prototype.operate = function (context, op, other) {
+ /*jshint noempty:false */
+ var value = this._operate(context, op, this.value, other.value),
+ unit = this.unit.clone();
+
+ if (op === '+' || op === '-') {
+ if (unit.numerator.length === 0 && unit.denominator.length === 0) {
+ unit.numerator = other.unit.numerator.slice(0);
+ unit.denominator = other.unit.denominator.slice(0);
+ } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
+ // do nothing
+ } else {
+ other = other.convertTo(this.unit.usedUnits());
+
+ if (context.strictUnits && other.unit.toString() !== unit.toString()) {
+ throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
+ "' and '" + other.unit.toString() + "'.");
+ }
+
+ value = this._operate(context, op, this.value, other.value);
+ }
+ } else if (op === '*') {
+ unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
+ unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
+ unit.cancel();
+ } else if (op === '/') {
+ unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
+ unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
+ unit.cancel();
+ }
+ return new Dimension(value, unit);
+};
+Dimension.prototype.compare = function (other) {
+ var a, b;
+
+ if (!(other instanceof Dimension)) {
+ return undefined;
+ }
+
+ if (this.unit.isEmpty() || other.unit.isEmpty()) {
+ a = this;
+ b = other;
+ } else {
+ a = this.unify();
+ b = other.unify();
+ if (a.unit.compare(b.unit) !== 0) {
+ return undefined;
+ }
+ }
+
+ return Node.numericCompare(a.value, b.value);
+};
+Dimension.prototype.unify = function () {
+ return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
+};
+Dimension.prototype.convertTo = function (conversions) {
+ var value = this.value, unit = this.unit.clone(),
+ i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
+
+ if (typeof conversions === 'string') {
+ for (i in unitConversions) {
+ if (unitConversions[i].hasOwnProperty(conversions)) {
+ derivedConversions = {};
+ derivedConversions[i] = conversions;
+ }
+ }
+ conversions = derivedConversions;
+ }
+ applyUnit = function (atomicUnit, denominator) {
+ /*jshint loopfunc:true */
+ if (group.hasOwnProperty(atomicUnit)) {
+ if (denominator) {
+ value = value / (group[atomicUnit] / group[targetUnit]);
+ } else {
+ value = value * (group[atomicUnit] / group[targetUnit]);
+ }
+
+ return targetUnit;
+ }
+
+ return atomicUnit;
+ };
+
+ for (groupName in conversions) {
+ if (conversions.hasOwnProperty(groupName)) {
+ targetUnit = conversions[groupName];
+ group = unitConversions[groupName];
+
+ unit.map(applyUnit);
+ }
+ }
+
+ unit.cancel();
+
+ return new Dimension(value, unit);
+};
+module.exports = Dimension;
+
+},{"../data/unit-conversions":13,"./color":47,"./node":67,"./unit":76}],54:[function(require,module,exports){
+var Node = require("./node"),
+ Ruleset = require("./ruleset");
+
+var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isReferenced) {
+ this.name = name;
+ this.value = value;
+ if (rules) {
+ this.rules = rules;
+ this.rules.allowImports = true;
+ }
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+ this.debugInfo = debugInfo;
+ this.isReferenced = isReferenced;
+};
+
+Directive.prototype = new Node();
+Directive.prototype.type = "Directive";
+Directive.prototype.accept = function (visitor) {
+ var value = this.value, rules = this.rules;
+ if (rules) {
+ this.rules = visitor.visit(rules);
+ }
+ if (value) {
+ this.value = visitor.visit(value);
+ }
+};
+Directive.prototype.isRulesetLike = function() {
+ return this.rules || !this.isCharset();
+};
+Directive.prototype.isCharset = function() {
+ return "@charset" === this.name;
+};
+Directive.prototype.genCSS = function (context, output) {
+ var value = this.value, rules = this.rules;
+ output.add(this.name, this.currentFileInfo, this.index);
+ if (value) {
+ output.add(' ');
+ value.genCSS(context, output);
+ }
+ if (rules) {
+ if (rules.type === "Ruleset") {
+ rules = [rules];
+ }
+ this.outputRuleset(context, output, rules);
+ } else {
+ output.add(';');
+ }
+};
+Directive.prototype.eval = function (context) {
+ var value = this.value, rules = this.rules;
+ if (value) {
+ value = value.eval(context);
+ }
+ if (rules) {
+ rules = rules.eval(context);
+ rules.root = true;
+ }
+ return new Directive(this.name, value, rules,
+ this.index, this.currentFileInfo, this.debugInfo, this.isReferenced);
+};
+Directive.prototype.variable = function (name) {
+ if (this.rules) {
+ return Ruleset.prototype.variable.call(this.rules, name);
+ }
+};
+Directive.prototype.find = function () {
+ if (this.rules) {
+ return Ruleset.prototype.find.apply(this.rules, arguments);
+ }
+};
+Directive.prototype.rulesets = function () {
+ if (this.rules) {
+ return Ruleset.prototype.rulesets.apply(this.rules);
+ }
+};
+Directive.prototype.markReferenced = function () {
+ var i, rules;
+ this.isReferenced = true;
+ if (this.rules) {
+ rules = this.rules.rules;
+ for (i = 0; i < rules.length; i++) {
+ if (rules[i].markReferenced) {
+ rules[i].markReferenced();
+ }
+ }
+ }
+};
+Directive.prototype.getIsReferenced = function () {
+ return !this.currentFileInfo || !this.currentFileInfo.reference || this.isReferenced;
+};
+Directive.prototype.outputRuleset = function (context, output, rules) {
+ var ruleCnt = rules.length, i;
+ context.tabLevel = (context.tabLevel | 0) + 1;
+
+ // Compressed
+ if (context.compress) {
+ output.add('{');
+ for (i = 0; i < ruleCnt; i++) {
+ rules[i].genCSS(context, output);
+ }
+ output.add('}');
+ context.tabLevel--;
+ return;
+ }
+
+ // Non-compressed
+ var tabSetStr = '\n' + Array(context.tabLevel).join(" "), tabRuleStr = tabSetStr + " ";
+ if (!ruleCnt) {
+ output.add(" {" + tabSetStr + '}');
+ } else {
+ output.add(" {" + tabRuleStr);
+ rules[0].genCSS(context, output);
+ for (i = 1; i < ruleCnt; i++) {
+ output.add(tabRuleStr);
+ rules[i].genCSS(context, output);
+ }
+ output.add(tabSetStr + '}');
+ }
+
+ context.tabLevel--;
+};
+module.exports = Directive;
+
+},{"./node":67,"./ruleset":73}],55:[function(require,module,exports){
+var Node = require("./node"),
+ Paren = require("./paren"),
+ Combinator = require("./combinator");
+
+var Element = function (combinator, value, index, currentFileInfo) {
+ this.combinator = combinator instanceof Combinator ?
+ combinator : new Combinator(combinator);
+
+ if (typeof value === 'string') {
+ this.value = value.trim();
+ } else if (value) {
+ this.value = value;
+ } else {
+ this.value = "";
+ }
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+Element.prototype = new Node();
+Element.prototype.type = "Element";
+Element.prototype.accept = function (visitor) {
+ var value = this.value;
+ this.combinator = visitor.visit(this.combinator);
+ if (typeof value === "object") {
+ this.value = visitor.visit(value);
+ }
+};
+Element.prototype.eval = function (context) {
+ return new Element(this.combinator,
+ this.value.eval ? this.value.eval(context) : this.value,
+ this.index,
+ this.currentFileInfo);
+};
+Element.prototype.genCSS = function (context, output) {
+ output.add(this.toCSS(context), this.currentFileInfo, this.index);
+};
+Element.prototype.toCSS = function (context) {
+ context = context || {};
+ var value = this.value, firstSelector = context.firstSelector;
+ if (value instanceof Paren) {
+ // selector in parens should not be affected by outer selector
+ // flags (breaks only interpolated selectors - see #1973)
+ context.firstSelector = true;
+ }
+ value = value.toCSS ? value.toCSS(context) : value;
+ context.firstSelector = firstSelector;
+ if (value === '' && this.combinator.value.charAt(0) === '&') {
+ return '';
+ } else {
+ return this.combinator.toCSS(context) + value;
+ }
+};
+module.exports = Element;
+
+},{"./combinator":48,"./node":67,"./paren":69}],56:[function(require,module,exports){
+var Node = require("./node"),
+ Paren = require("./paren"),
+ Comment = require("./comment");
+
+var Expression = function (value) {
+ this.value = value;
+ if (!value) {
+ throw new Error("Expression requires an array parameter");
+ }
+};
+Expression.prototype = new Node();
+Expression.prototype.type = "Expression";
+Expression.prototype.accept = function (visitor) {
+ this.value = visitor.visitArray(this.value);
+};
+Expression.prototype.eval = function (context) {
+ var returnValue,
+ inParenthesis = this.parens && !this.parensInOp,
+ doubleParen = false;
+ if (inParenthesis) {
+ context.inParenthesis();
+ }
+ if (this.value.length > 1) {
+ returnValue = new Expression(this.value.map(function (e) {
+ return e.eval(context);
+ }));
+ } else if (this.value.length === 1) {
+ if (this.value[0].parens && !this.value[0].parensInOp) {
+ doubleParen = true;
+ }
+ returnValue = this.value[0].eval(context);
+ } else {
+ returnValue = this;
+ }
+ if (inParenthesis) {
+ context.outOfParenthesis();
+ }
+ if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) {
+ returnValue = new Paren(returnValue);
+ }
+ return returnValue;
+};
+Expression.prototype.genCSS = function (context, output) {
+ for (var i = 0; i < this.value.length; i++) {
+ this.value[i].genCSS(context, output);
+ if (i + 1 < this.value.length) {
+ output.add(" ");
+ }
+ }
+};
+Expression.prototype.throwAwayComments = function () {
+ this.value = this.value.filter(function(v) {
+ return !(v instanceof Comment);
+ });
+};
+module.exports = Expression;
+
+},{"./comment":49,"./node":67,"./paren":69}],57:[function(require,module,exports){
+var Node = require("./node");
+
+var Extend = function Extend(selector, option, index) {
+ this.selector = selector;
+ this.option = option;
+ this.index = index;
+ this.object_id = Extend.next_id++;
+ this.parent_ids = [this.object_id];
+
+ switch(option) {
+ case "all":
+ this.allowBefore = true;
+ this.allowAfter = true;
+ break;
+ default:
+ this.allowBefore = false;
+ this.allowAfter = false;
+ break;
+ }
+};
+Extend.next_id = 0;
+
+Extend.prototype = new Node();
+Extend.prototype.type = "Extend";
+Extend.prototype.accept = function (visitor) {
+ this.selector = visitor.visit(this.selector);
+};
+Extend.prototype.eval = function (context) {
+ return new Extend(this.selector.eval(context), this.option, this.index);
+};
+Extend.prototype.clone = function (context) {
+ return new Extend(this.selector, this.option, this.index);
+};
+Extend.prototype.findSelfSelectors = function (selectors) {
+ var selfElements = [],
+ i,
+ selectorElements;
+
+ for (i = 0; i < selectors.length; i++) {
+ selectorElements = selectors[i].elements;
+ // duplicate the logic in genCSS function inside the selector node.
+ // future TODO - move both logics into the selector joiner visitor
+ if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
+ selectorElements[0].combinator.value = ' ';
+ }
+ selfElements = selfElements.concat(selectors[i].elements);
+ }
+
+ this.selfSelectors = [{ elements: selfElements }];
+};
+module.exports = Extend;
+
+},{"./node":67}],58:[function(require,module,exports){
+var Node = require("./node"),
+ Media = require("./media"),
+ URL = require("./url"),
+ Quoted = require("./quoted"),
+ Ruleset = require("./ruleset"),
+ Anonymous = require("./anonymous");
+
+//
+// CSS @import node
+//
+// The general strategy here is that we don't want to wait
+// for the parsing to be completed, before we start importing
+// the file. That's because in the context of a browser,
+// most of the time will be spent waiting for the server to respond.
+//
+// On creation, we push the import path to our import queue, though
+// `import,push`, we also pass it a callback, which it'll call once
+// the file has been fetched, and parsed.
+//
+var Import = function (path, features, options, index, currentFileInfo) {
+ this.options = options;
+ this.index = index;
+ this.path = path;
+ this.features = features;
+ this.currentFileInfo = currentFileInfo;
+
+ if (this.options.less !== undefined || this.options.inline) {
+ this.css = !this.options.less || this.options.inline;
+ } else {
+ var pathValue = this.getPath();
+ if (pathValue && /[#\.\&\?\/]css([\?;].*)?$/.test(pathValue)) {
+ this.css = true;
+ }
+ }
+};
+
+//
+// The actual import node doesn't return anything, when converted to CSS.
+// The reason is that it's used at the evaluation stage, so that the rules
+// it imports can be treated like any other rules.
+//
+// In `eval`, we make sure all Import nodes get evaluated, recursively, so
+// we end up with a flat structure, which can easily be imported in the parent
+// ruleset.
+//
+Import.prototype = new Node();
+Import.prototype.type = "Import";
+Import.prototype.accept = function (visitor) {
+ if (this.features) {
+ this.features = visitor.visit(this.features);
+ }
+ this.path = visitor.visit(this.path);
+ if (!this.options.inline && this.root) {
+ this.root = visitor.visit(this.root);
+ }
+};
+Import.prototype.genCSS = function (context, output) {
+ if (this.css) {
+ output.add("@import ", this.currentFileInfo, this.index);
+ this.path.genCSS(context, output);
+ if (this.features) {
+ output.add(" ");
+ this.features.genCSS(context, output);
+ }
+ output.add(';');
+ }
+};
+Import.prototype.getPath = function () {
+ if (this.path instanceof Quoted) {
+ return this.path.value;
+ } else if (this.path instanceof URL) {
+ return this.path.value.value;
+ }
+ return null;
+};
+Import.prototype.isVariableImport = function () {
+ var path = this.path;
+ if (path instanceof URL) {
+ path = path.value;
+ }
+ if (path instanceof Quoted) {
+ return path.containsVariables();
+ }
+
+ return true;
+};
+Import.prototype.evalForImport = function (context) {
+ var path = this.path;
+ if (path instanceof URL) {
+ path = path.value;
+ }
+ return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo);
+};
+Import.prototype.evalPath = function (context) {
+ var path = this.path.eval(context);
+ var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
+
+ if (!(path instanceof URL)) {
+ if (rootpath) {
+ var pathValue = path.value;
+ // Add the base path if the import is relative
+ if (pathValue && context.isPathRelative(pathValue)) {
+ path.value = rootpath + pathValue;
+ }
+ }
+ path.value = context.normalizePath(path.value);
+ }
+
+ return path;
+};
+Import.prototype.eval = function (context) {
+ var ruleset, features = this.features && this.features.eval(context);
+
+ if (this.skip) {
+ if (typeof this.skip === "function") {
+ this.skip = this.skip();
+ }
+ if (this.skip) {
+ return [];
+ }
+ }
+
+ if (this.options.inline) {
+ var contents = new Anonymous(this.root, 0, {filename: this.importedFilename}, true, true);
+ return this.features ? new Media([contents], this.features.value) : [contents];
+ } else if (this.css) {
+ var newImport = new Import(this.evalPath(context), features, this.options, this.index);
+ if (!newImport.css && this.error) {
+ throw this.error;
+ }
+ return newImport;
+ } else {
+ ruleset = new Ruleset(null, this.root.rules.slice(0));
+
+ ruleset.evalImports(context);
+
+ return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules;
+ }
+};
+module.exports = Import;
+
+},{"./anonymous":43,"./media":63,"./node":67,"./quoted":70,"./ruleset":73,"./url":77}],59:[function(require,module,exports){
+var tree = {};
+
+tree.Node = require('./node');
+tree.Alpha = require('./alpha');
+tree.Color = require('./color');
+tree.Directive = require('./directive');
+tree.DetachedRuleset = require('./detached-ruleset');
+tree.Operation = require('./operation');
+tree.Dimension = require('./dimension');
+tree.Unit = require('./unit');
+tree.Keyword = require('./keyword');
+tree.Variable = require('./variable');
+tree.Ruleset = require('./ruleset');
+tree.Element = require('./element');
+tree.Attribute = require('./attribute');
+tree.Combinator = require('./combinator');
+tree.Selector = require('./selector');
+tree.Quoted = require('./quoted');
+tree.Expression = require('./expression');
+tree.Rule = require('./rule');
+tree.Call = require('./call');
+tree.URL = require('./url');
+tree.Import = require('./import');
+tree.mixin = {
+ Call: require('./mixin-call'),
+ Definition: require('./mixin-definition')
+};
+tree.Comment = require('./comment');
+tree.Anonymous = require('./anonymous');
+tree.Value = require('./value');
+tree.JavaScript = require('./javascript');
+tree.Assignment = require('./assignment');
+tree.Condition = require('./condition');
+tree.Paren = require('./paren');
+tree.Media = require('./media');
+tree.UnicodeDescriptor = require('./unicode-descriptor');
+tree.Negative = require('./negative');
+tree.Extend = require('./extend');
+tree.RulesetCall = require('./ruleset-call');
+
+module.exports = tree;
+
+},{"./alpha":42,"./anonymous":43,"./assignment":44,"./attribute":45,"./call":46,"./color":47,"./combinator":48,"./comment":49,"./condition":50,"./detached-ruleset":52,"./dimension":53,"./directive":54,"./element":55,"./expression":56,"./extend":57,"./import":58,"./javascript":60,"./keyword":62,"./media":63,"./mixin-call":64,"./mixin-definition":65,"./negative":66,"./node":67,"./operation":68,"./paren":69,"./quoted":70,"./rule":71,"./ruleset":73,"./ruleset-call":72,"./selector":74,"./unicode-descriptor":75,"./unit":76,"./url":77,"./value":78,"./variable":79}],60:[function(require,module,exports){
+var JsEvalNode = require("./js-eval-node"),
+ Dimension = require("./dimension"),
+ Quoted = require("./quoted"),
+ Anonymous = require("./anonymous");
+
+var JavaScript = function (string, escaped, index, currentFileInfo) {
+ this.escaped = escaped;
+ this.expression = string;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+JavaScript.prototype = new JsEvalNode();
+JavaScript.prototype.type = "JavaScript";
+JavaScript.prototype.eval = function(context) {
+ var result = this.evaluateJavaScript(this.expression, context);
+
+ if (typeof result === 'number') {
+ return new Dimension(result);
+ } else if (typeof result === 'string') {
+ return new Quoted('"' + result + '"', result, this.escaped, this.index);
+ } else if (Array.isArray(result)) {
+ return new Anonymous(result.join(', '));
+ } else {
+ return new Anonymous(result);
+ }
+};
+
+module.exports = JavaScript;
+
+},{"./anonymous":43,"./dimension":53,"./js-eval-node":61,"./quoted":70}],61:[function(require,module,exports){
+var Node = require("./node"),
+ Variable = require("./variable");
+
+var JsEvalNode = function() {
+};
+JsEvalNode.prototype = new Node();
+
+JsEvalNode.prototype.evaluateJavaScript = function (expression, context) {
+ var result,
+ that = this,
+ evalContext = {};
+
+ if (context.javascriptEnabled !== undefined && !context.javascriptEnabled) {
+ throw { message: "You are using JavaScript, which has been disabled.",
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+
+ expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
+ return that.jsify(new Variable('@' + name, that.index, that.currentFileInfo).eval(context));
+ });
+
+ try {
+ expression = new Function('return (' + expression + ')');
+ } catch (e) {
+ throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+
+ var variables = context.frames[0].variables();
+ for (var k in variables) {
+ if (variables.hasOwnProperty(k)) {
+ /*jshint loopfunc:true */
+ evalContext[k.slice(1)] = {
+ value: variables[k].value,
+ toJS: function () {
+ return this.value.eval(context).toCSS();
+ }
+ };
+ }
+ }
+
+ try {
+ result = expression.call(evalContext);
+ } catch (e) {
+ throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+ return result;
+};
+JsEvalNode.prototype.jsify = function (obj) {
+ if (Array.isArray(obj.value) && (obj.value.length > 1)) {
+ return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
+ } else {
+ return obj.toCSS();
+ }
+};
+
+module.exports = JsEvalNode;
+
+},{"./node":67,"./variable":79}],62:[function(require,module,exports){
+var Node = require("./node");
+
+var Keyword = function (value) { this.value = value; };
+Keyword.prototype = new Node();
+Keyword.prototype.type = "Keyword";
+Keyword.prototype.genCSS = function (context, output) {
+ if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
+ output.add(this.value);
+};
+
+Keyword.True = new Keyword('true');
+Keyword.False = new Keyword('false');
+
+module.exports = Keyword;
+
+},{"./node":67}],63:[function(require,module,exports){
+var Ruleset = require("./ruleset"),
+ Value = require("./value"),
+ Element = require("./element"),
+ Selector = require("./selector"),
+ Anonymous = require("./anonymous"),
+ Expression = require("./expression"),
+ Directive = require("./directive");
+
+var Media = function (value, features, index, currentFileInfo) {
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+
+ var selectors = this.emptySelectors();
+
+ this.features = new Value(features);
+ this.rules = [new Ruleset(selectors, value)];
+ this.rules[0].allowImports = true;
+};
+Media.prototype = new Directive();
+Media.prototype.type = "Media";
+Media.prototype.isRulesetLike = true;
+Media.prototype.accept = function (visitor) {
+ if (this.features) {
+ this.features = visitor.visit(this.features);
+ }
+ if (this.rules) {
+ this.rules = visitor.visitArray(this.rules);
+ }
+};
+Media.prototype.genCSS = function (context, output) {
+ output.add('@media ', this.currentFileInfo, this.index);
+ this.features.genCSS(context, output);
+ this.outputRuleset(context, output, this.rules);
+};
+Media.prototype.eval = function (context) {
+ if (!context.mediaBlocks) {
+ context.mediaBlocks = [];
+ context.mediaPath = [];
+ }
+
+ var media = new Media(null, [], this.index, this.currentFileInfo);
+ if (this.debugInfo) {
+ this.rules[0].debugInfo = this.debugInfo;
+ media.debugInfo = this.debugInfo;
+ }
+ var strictMathBypass = false;
+ if (!context.strictMath) {
+ strictMathBypass = true;
+ context.strictMath = true;
+ }
+ try {
+ media.features = this.features.eval(context);
+ }
+ finally {
+ if (strictMathBypass) {
+ context.strictMath = false;
+ }
+ }
+
+ context.mediaPath.push(media);
+ context.mediaBlocks.push(media);
+
+ context.frames.unshift(this.rules[0]);
+ media.rules = [this.rules[0].eval(context)];
+ context.frames.shift();
+
+ context.mediaPath.pop();
+
+ return context.mediaPath.length === 0 ? media.evalTop(context) :
+ media.evalNested(context);
+};
+//TODO merge with directive
+Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); };
+Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); };
+Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); };
+Media.prototype.emptySelectors = function() {
+ var el = new Element('', '&', this.index, this.currentFileInfo),
+ sels = [new Selector([el], null, null, this.index, this.currentFileInfo)];
+ sels[0].mediaEmpty = true;
+ return sels;
+};
+Media.prototype.markReferenced = function () {
+ var i, rules = this.rules[0].rules;
+ this.rules[0].markReferenced();
+ this.isReferenced = true;
+ for (i = 0; i < rules.length; i++) {
+ if (rules[i].markReferenced) {
+ rules[i].markReferenced();
+ }
+ }
+};
+Media.prototype.evalTop = function (context) {
+ var result = this;
+
+ // Render all dependent Media blocks.
+ if (context.mediaBlocks.length > 1) {
+ var selectors = this.emptySelectors();
+ result = new Ruleset(selectors, context.mediaBlocks);
+ result.multiMedia = true;
+ }
+
+ delete context.mediaBlocks;
+ delete context.mediaPath;
+
+ return result;
+};
+Media.prototype.evalNested = function (context) {
+ var i, value,
+ path = context.mediaPath.concat([this]);
+
+ // Extract the media-query conditions separated with `,` (OR).
+ for (i = 0; i < path.length; i++) {
+ value = path[i].features instanceof Value ?
+ path[i].features.value : path[i].features;
+ path[i] = Array.isArray(value) ? value : [value];
+ }
+
+ // Trace all permutations to generate the resulting media-query.
+ //
+ // (a, b and c) with nested (d, e) ->
+ // a and d
+ // a and e
+ // b and c and d
+ // b and c and e
+ this.features = new Value(this.permute(path).map(function (path) {
+ path = path.map(function (fragment) {
+ return fragment.toCSS ? fragment : new Anonymous(fragment);
+ });
+
+ for (i = path.length - 1; i > 0; i--) {
+ path.splice(i, 0, new Anonymous("and"));
+ }
+
+ return new Expression(path);
+ }));
+
+ // Fake a tree-node that doesn't output anything.
+ return new Ruleset([], []);
+};
+Media.prototype.permute = function (arr) {
+ if (arr.length === 0) {
+ return [];
+ } else if (arr.length === 1) {
+ return arr[0];
+ } else {
+ var result = [];
+ var rest = this.permute(arr.slice(1));
+ for (var i = 0; i < rest.length; i++) {
+ for (var j = 0; j < arr[0].length; j++) {
+ result.push([arr[0][j]].concat(rest[i]));
+ }
+ }
+ return result;
+ }
+};
+Media.prototype.bubbleSelectors = function (selectors) {
+ if (!selectors) {
+ return;
+ }
+ this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])];
+};
+module.exports = Media;
+
+},{"./anonymous":43,"./directive":54,"./element":55,"./expression":56,"./ruleset":73,"./selector":74,"./value":78}],64:[function(require,module,exports){
+var Node = require("./node"),
+ Selector = require("./selector"),
+ MixinDefinition = require("./mixin-definition"),
+ defaultFunc = require("../functions/default");
+
+var MixinCall = function (elements, args, index, currentFileInfo, important) {
+ this.selector = new Selector(elements);
+ this.arguments = (args && args.length) ? args : null;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+ this.important = important;
+};
+MixinCall.prototype = new Node();
+MixinCall.prototype.type = "MixinCall";
+MixinCall.prototype.accept = function (visitor) {
+ if (this.selector) {
+ this.selector = visitor.visit(this.selector);
+ }
+ if (this.arguments) {
+ this.arguments = visitor.visitArray(this.arguments);
+ }
+};
+MixinCall.prototype.eval = function (context) {
+ var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
+ candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase = -1,
+ defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter;
+
+ function calcDefGroup(mixin, mixinPath) {
+ var p, namespace;
+
+ for (f = 0; f < 2; f++) {
+ conditionResult[f] = true;
+ defaultFunc.value(f);
+ for (p = 0; p < mixinPath.length && conditionResult[f]; p++) {
+ namespace = mixinPath[p];
+ if (namespace.matchCondition) {
+ conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context);
+ }
+ }
+ if (mixin.matchCondition) {
+ conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context);
+ }
+ }
+ if (conditionResult[0] || conditionResult[1]) {
+ if (conditionResult[0] != conditionResult[1]) {
+ return conditionResult[1] ?
+ defTrue : defFalse;
+ }
+
+ return defNone;
+ }
+ return defFalseEitherCase;
+ }
+
+ args = this.arguments && this.arguments.map(function (a) {
+ return { name: a.name, value: a.value.eval(context) };
+ });
+
+ noArgumentsFilter = function(rule) {return rule.matchArgs(null, context);};
+
+ for (i = 0; i < context.frames.length; i++) {
+ if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) {
+ isOneFound = true;
+
+ // To make `default()` function independent of definition order we have two "subpasses" here.
+ // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
+ // and build candidate list with corresponding flags. Then, when we know all possible matches,
+ // we make a final decision.
+
+ for (m = 0; m < mixins.length; m++) {
+ mixin = mixins[m].rule;
+ mixinPath = mixins[m].path;
+ isRecursive = false;
+ for (f = 0; f < context.frames.length; f++) {
+ if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) {
+ isRecursive = true;
+ break;
+ }
+ }
+ if (isRecursive) {
+ continue;
+ }
+
+ if (mixin.matchArgs(args, context)) {
+ candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)};
+
+ if (candidate.group !== defFalseEitherCase) {
+ candidates.push(candidate);
+ }
+
+ match = true;
+ }
+ }
+
+ defaultFunc.reset();
+
+ count = [0, 0, 0];
+ for (m = 0; m < candidates.length; m++) {
+ count[candidates[m].group]++;
+ }
+
+ if (count[defNone] > 0) {
+ defaultResult = defFalse;
+ } else {
+ defaultResult = defTrue;
+ if ((count[defTrue] + count[defFalse]) > 1) {
+ throw { type: 'Runtime',
+ message: 'Ambiguous use of `default()` found when matching for `' + this.format(args) + '`',
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+ }
+
+ for (m = 0; m < candidates.length; m++) {
+ candidate = candidates[m].group;
+ if ((candidate === defNone) || (candidate === defaultResult)) {
+ try {
+ mixin = candidates[m].mixin;
+ if (!(mixin instanceof MixinDefinition)) {
+ originalRuleset = mixin.originalRuleset || mixin;
+ mixin = new MixinDefinition("", [], mixin.rules, null, false);
+ mixin.originalRuleset = originalRuleset;
+ }
+ Array.prototype.push.apply(
+ rules, mixin.evalCall(context, args, this.important).rules);
+ } catch (e) {
+ throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
+ }
+ }
+ }
+
+ if (match) {
+ if (!this.currentFileInfo || !this.currentFileInfo.reference) {
+ for (i = 0; i < rules.length; i++) {
+ rule = rules[i];
+ if (rule.markReferenced) {
+ rule.markReferenced();
+ }
+ }
+ }
+ return rules;
+ }
+ }
+ }
+ if (isOneFound) {
+ throw { type: 'Runtime',
+ message: 'No matching definition was found for `' + this.format(args) + '`',
+ index: this.index, filename: this.currentFileInfo.filename };
+ } else {
+ throw { type: 'Name',
+ message: this.selector.toCSS().trim() + " is undefined",
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+};
+MixinCall.prototype.format = function (args) {
+ return this.selector.toCSS().trim() + '(' +
+ (args ? args.map(function (a) {
+ var argValue = "";
+ if (a.name) {
+ argValue += a.name + ":";
+ }
+ if (a.value.toCSS) {
+ argValue += a.value.toCSS();
+ } else {
+ argValue += "???";
+ }
+ return argValue;
+ }).join(', ') : "") + ")";
+};
+module.exports = MixinCall;
+
+},{"../functions/default":19,"./mixin-definition":65,"./node":67,"./selector":74}],65:[function(require,module,exports){
+var Selector = require("./selector"),
+ Element = require("./element"),
+ Ruleset = require("./ruleset"),
+ Rule = require("./rule"),
+ Expression = require("./expression"),
+ contexts = require("../contexts");
+
+var Definition = function (name, params, rules, condition, variadic, frames) {
+ this.name = name;
+ this.selectors = [new Selector([new Element(null, name, this.index, this.currentFileInfo)])];
+ this.params = params;
+ this.condition = condition;
+ this.variadic = variadic;
+ this.arity = params.length;
+ this.rules = rules;
+ this._lookups = {};
+ this.required = params.reduce(function (count, p) {
+ if (!p.name || (p.name && !p.value)) {
+ return count + 1;
+ }
+ else {
+ return count;
+ }
+ }, 0);
+ this.frames = frames;
+};
+Definition.prototype = new Ruleset();
+Definition.prototype.type = "MixinDefinition";
+Definition.prototype.evalFirst = true;
+Definition.prototype.accept = function (visitor) {
+ if (this.params && this.params.length) {
+ this.params = visitor.visitArray(this.params);
+ }
+ this.rules = visitor.visitArray(this.rules);
+ if (this.condition) {
+ this.condition = visitor.visit(this.condition);
+ }
+};
+Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) {
+ /*jshint boss:true */
+ var frame = new Ruleset(null, null),
+ varargs, arg,
+ params = this.params.slice(0),
+ i, j, val, name, isNamedFound, argIndex, argsLength = 0;
+
+ mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
+
+ if (args) {
+ args = args.slice(0);
+ argsLength = args.length;
+
+ for (i = 0; i < argsLength; i++) {
+ arg = args[i];
+ if (name = (arg && arg.name)) {
+ isNamedFound = false;
+ for (j = 0; j < params.length; j++) {
+ if (!evaldArguments[j] && name === params[j].name) {
+ evaldArguments[j] = arg.value.eval(context);
+ frame.prependRule(new Rule(name, arg.value.eval(context)));
+ isNamedFound = true;
+ break;
+ }
+ }
+ if (isNamedFound) {
+ args.splice(i, 1);
+ i--;
+ continue;
+ } else {
+ throw { type: 'Runtime', message: "Named argument for " + this.name +
+ ' ' + args[i].name + ' not found' };
+ }
+ }
+ }
+ }
+ argIndex = 0;
+ for (i = 0; i < params.length; i++) {
+ if (evaldArguments[i]) { continue; }
+
+ arg = args && args[argIndex];
+
+ if (name = params[i].name) {
+ if (params[i].variadic) {
+ varargs = [];
+ for (j = argIndex; j < argsLength; j++) {
+ varargs.push(args[j].value.eval(context));
+ }
+ frame.prependRule(new Rule(name, new Expression(varargs).eval(context)));
+ } else {
+ val = arg && arg.value;
+ if (val) {
+ val = val.eval(context);
+ } else if (params[i].value) {
+ val = params[i].value.eval(mixinEnv);
+ frame.resetCache();
+ } else {
+ throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
+ ' (' + argsLength + ' for ' + this.arity + ')' };
+ }
+
+ frame.prependRule(new Rule(name, val));
+ evaldArguments[i] = val;
+ }
+ }
+
+ if (params[i].variadic && args) {
+ for (j = argIndex; j < argsLength; j++) {
+ evaldArguments[j] = args[j].value.eval(context);
+ }
+ }
+ argIndex++;
+ }
+
+ return frame;
+};
+Definition.prototype.makeImportant = function() {
+ var rules = !this.rules ? this.rules : this.rules.map(function (r) {
+ if (r.makeImportant) {
+ return r.makeImportant(true);
+ } else {
+ return r;
+ }
+ });
+ var result = new Definition (this.name, this.params, rules, this.condition, this.variadic, this.frames);
+ return result;
+};
+Definition.prototype.eval = function (context) {
+ return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || context.frames.slice(0));
+};
+Definition.prototype.evalCall = function (context, args, important) {
+ var _arguments = [],
+ mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames,
+ frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments),
+ rules, ruleset;
+
+ frame.prependRule(new Rule('@arguments', new Expression(_arguments).eval(context)));
+
+ rules = this.rules.slice(0);
+
+ ruleset = new Ruleset(null, rules);
+ ruleset.originalRuleset = this;
+ ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames)));
+ if (important) {
+ ruleset = ruleset.makeImportant();
+ }
+ return ruleset;
+};
+Definition.prototype.matchCondition = function (args, context) {
+ if (this.condition && !this.condition.eval(
+ new contexts.Eval(context,
+ [this.evalParams(context, /* the parameter variables*/
+ new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
+ .concat(this.frames) // the parent namespace/mixin frames
+ .concat(context.frames)))) { // the current environment frames
+ return false;
+ }
+ return true;
+};
+Definition.prototype.matchArgs = function (args, context) {
+ var argsLength = (args && args.length) || 0, len;
+
+ if (! this.variadic) {
+ if (argsLength < this.required) {
+ return false;
+ }
+ if (argsLength > this.params.length) {
+ return false;
+ }
+ } else {
+ if (argsLength < (this.required - 1)) {
+ return false;
+ }
+ }
+
+ len = Math.min(argsLength, this.arity);
+
+ for (var i = 0; i < len; i++) {
+ if (!this.params[i].name && !this.params[i].variadic) {
+ if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) {
+ return false;
+ }
+ }
+ }
+ return true;
+};
+module.exports = Definition;
+
+},{"../contexts":10,"./element":55,"./expression":56,"./rule":71,"./ruleset":73,"./selector":74}],66:[function(require,module,exports){
+var Node = require("./node"),
+ Operation = require("./operation"),
+ Dimension = require("./dimension");
+
+var Negative = function (node) {
+ this.value = node;
+};
+Negative.prototype = new Node();
+Negative.prototype.type = "Negative";
+Negative.prototype.genCSS = function (context, output) {
+ output.add('-');
+ this.value.genCSS(context, output);
+};
+Negative.prototype.eval = function (context) {
+ if (context.isMathOn()) {
+ return (new Operation('*', [new Dimension(-1), this.value])).eval(context);
+ }
+ return new Negative(this.value.eval(context));
+};
+module.exports = Negative;
+
+},{"./dimension":53,"./node":67,"./operation":68}],67:[function(require,module,exports){
+var Node = function() {
+};
+Node.prototype.toCSS = function (context) {
+ var strs = [];
+ this.genCSS(context, {
+ add: function(chunk, fileInfo, index) {
+ strs.push(chunk);
+ },
+ isEmpty: function () {
+ return strs.length === 0;
+ }
+ });
+ return strs.join('');
+};
+Node.prototype.genCSS = function (context, output) {
+ output.add(this.value);
+};
+Node.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+Node.prototype.eval = function () { return this; };
+Node.prototype._operate = function (context, op, a, b) {
+ switch (op) {
+ case '+': return a + b;
+ case '-': return a - b;
+ case '*': return a * b;
+ case '/': return a / b;
+ }
+};
+Node.prototype.fround = function(context, value) {
+ var precision = context && context.numPrecision;
+ //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
+ return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
+};
+Node.compare = function (a, b) {
+ /* returns:
+ -1: a < b
+ 0: a = b
+ 1: a > b
+ and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */
+
+ if ((a.compare) &&
+ // for "symmetric results" force toCSS-based comparison
+ // of Quoted or Anonymous if either value is one of those
+ !(b.type === "Quoted" || b.type === "Anonymous")) {
+ return a.compare(b);
+ } else if (b.compare) {
+ return -b.compare(a);
+ } else if (a.type !== b.type) {
+ return undefined;
+ }
+
+ a = a.value;
+ b = b.value;
+ if (!Array.isArray(a)) {
+ return a === b ? 0 : undefined;
+ }
+ if (a.length !== b.length) {
+ return undefined;
+ }
+ for (var i = 0; i < a.length; i++) {
+ if (Node.compare(a[i], b[i]) !== 0) {
+ return undefined;
+ }
+ }
+ return 0;
+};
+
+Node.numericCompare = function (a, b) {
+ return a < b ? -1
+ : a === b ? 0
+ : a > b ? 1 : undefined;
+};
+module.exports = Node;
+
+},{}],68:[function(require,module,exports){
+var Node = require("./node"),
+ Color = require("./color"),
+ Dimension = require("./dimension");
+
+var Operation = function (op, operands, isSpaced) {
+ this.op = op.trim();
+ this.operands = operands;
+ this.isSpaced = isSpaced;
+};
+Operation.prototype = new Node();
+Operation.prototype.type = "Operation";
+Operation.prototype.accept = function (visitor) {
+ this.operands = visitor.visit(this.operands);
+};
+Operation.prototype.eval = function (context) {
+ var a = this.operands[0].eval(context),
+ b = this.operands[1].eval(context);
+
+ if (context.isMathOn()) {
+ if (a instanceof Dimension && b instanceof Color) {
+ a = a.toColor();
+ }
+ if (b instanceof Dimension && a instanceof Color) {
+ b = b.toColor();
+ }
+ if (!a.operate) {
+ throw { type: "Operation",
+ message: "Operation on an invalid type" };
+ }
+
+ return a.operate(context, this.op, b);
+ } else {
+ return new Operation(this.op, [a, b], this.isSpaced);
+ }
+};
+Operation.prototype.genCSS = function (context, output) {
+ this.operands[0].genCSS(context, output);
+ if (this.isSpaced) {
+ output.add(" ");
+ }
+ output.add(this.op);
+ if (this.isSpaced) {
+ output.add(" ");
+ }
+ this.operands[1].genCSS(context, output);
+};
+
+module.exports = Operation;
+
+},{"./color":47,"./dimension":53,"./node":67}],69:[function(require,module,exports){
+var Node = require("./node");
+
+var Paren = function (node) {
+ this.value = node;
+};
+Paren.prototype = new Node();
+Paren.prototype.type = "Paren";
+Paren.prototype.genCSS = function (context, output) {
+ output.add('(');
+ this.value.genCSS(context, output);
+ output.add(')');
+};
+Paren.prototype.eval = function (context) {
+ return new Paren(this.value.eval(context));
+};
+module.exports = Paren;
+
+},{"./node":67}],70:[function(require,module,exports){
+var Node = require("./node"),
+ JsEvalNode = require("./js-eval-node"),
+ Variable = require("./variable");
+
+var Quoted = function (str, content, escaped, index, currentFileInfo) {
+ this.escaped = (escaped == null) ? true : escaped;
+ this.value = content || '';
+ this.quote = str.charAt(0);
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+Quoted.prototype = new JsEvalNode();
+Quoted.prototype.type = "Quoted";
+Quoted.prototype.genCSS = function (context, output) {
+ if (!this.escaped) {
+ output.add(this.quote, this.currentFileInfo, this.index);
+ }
+ output.add(this.value);
+ if (!this.escaped) {
+ output.add(this.quote);
+ }
+};
+Quoted.prototype.containsVariables = function() {
+ return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/);
+};
+Quoted.prototype.eval = function (context) {
+ var that = this, value = this.value;
+ var javascriptReplacement = function (_, exp) {
+ return String(that.evaluateJavaScript(exp, context));
+ };
+ var interpolationReplacement = function (_, name) {
+ var v = new Variable('@' + name, that.index, that.currentFileInfo).eval(context, true);
+ return (v instanceof Quoted) ? v.value : v.toCSS();
+ };
+ function iterativeReplace(value, regexp, replacementFnc) {
+ var evaluatedValue = value;
+ do {
+ value = evaluatedValue;
+ evaluatedValue = value.replace(regexp, replacementFnc);
+ } while (value !== evaluatedValue);
+ return evaluatedValue;
+ }
+ value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement);
+ value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement);
+ return new Quoted(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
+};
+Quoted.prototype.compare = function (other) {
+ // when comparing quoted strings allow the quote to differ
+ if (other.type === "Quoted" && !this.escaped && !other.escaped) {
+ return Node.numericCompare(this.value, other.value);
+ } else {
+ return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
+ }
+};
+module.exports = Quoted;
+
+},{"./js-eval-node":61,"./node":67,"./variable":79}],71:[function(require,module,exports){
+var Node = require("./node"),
+ Value = require("./value"),
+ Keyword = require("./keyword");
+
+var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
+ this.name = name;
+ this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
+ this.important = important ? ' ' + important.trim() : '';
+ this.merge = merge;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+ this.inline = inline || false;
+ this.variable = (variable !== undefined) ? variable
+ : (name.charAt && (name.charAt(0) === '@'));
+};
+
+function evalName(context, name) {
+ var value = "", i, n = name.length,
+ output = {add: function (s) {value += s;}};
+ for (i = 0; i < n; i++) {
+ name[i].eval(context).genCSS(context, output);
+ }
+ return value;
+}
+
+Rule.prototype = new Node();
+Rule.prototype.type = "Rule";
+Rule.prototype.genCSS = function (context, output) {
+ output.add(this.name + (context.compress ? ':' : ': '), this.currentFileInfo, this.index);
+ try {
+ this.value.genCSS(context, output);
+ }
+ catch(e) {
+ e.index = this.index;
+ e.filename = this.currentFileInfo.filename;
+ throw e;
+ }
+ output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this.currentFileInfo, this.index);
+};
+Rule.prototype.eval = function (context) {
+ var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable;
+ if (typeof name !== "string") {
+ // expand 'primitive' name directly to get
+ // things faster (~10% for benchmark.less):
+ name = (name.length === 1) && (name[0] instanceof Keyword) ?
+ name[0].value : evalName(context, name);
+ variable = false; // never treat expanded interpolation as new variable name
+ }
+ if (name === "font" && !context.strictMath) {
+ strictMathBypass = true;
+ context.strictMath = true;
+ }
+ try {
+ context.importantScope.push({});
+ evaldValue = this.value.eval(context);
+
+ if (!this.variable && evaldValue.type === "DetachedRuleset") {
+ throw { message: "Rulesets cannot be evaluated on a property.",
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+ var important = this.important,
+ importantResult = context.importantScope.pop();
+ if (!important && importantResult.important) {
+ important = importantResult.important;
+ }
+
+ return new Rule(name,
+ evaldValue,
+ important,
+ this.merge,
+ this.index, this.currentFileInfo, this.inline,
+ variable);
+ }
+ catch(e) {
+ if (typeof e.index !== 'number') {
+ e.index = this.index;
+ e.filename = this.currentFileInfo.filename;
+ }
+ throw e;
+ }
+ finally {
+ if (strictMathBypass) {
+ context.strictMath = false;
+ }
+ }
+};
+Rule.prototype.makeImportant = function () {
+ return new Rule(this.name,
+ this.value,
+ "!important",
+ this.merge,
+ this.index, this.currentFileInfo, this.inline);
+};
+
+module.exports = Rule;
+
+},{"./keyword":62,"./node":67,"./value":78}],72:[function(require,module,exports){
+var Node = require("./node"),
+ Variable = require("./variable");
+
+var RulesetCall = function (variable) {
+ this.variable = variable;
+};
+RulesetCall.prototype = new Node();
+RulesetCall.prototype.type = "RulesetCall";
+RulesetCall.prototype.eval = function (context) {
+ var detachedRuleset = new Variable(this.variable).eval(context);
+ return detachedRuleset.callEval(context);
+};
+module.exports = RulesetCall;
+
+},{"./node":67,"./variable":79}],73:[function(require,module,exports){
+var Node = require("./node"),
+ Rule = require("./rule"),
+ Selector = require("./selector"),
+ Element = require("./element"),
+ Paren = require("./paren"),
+ contexts = require("../contexts"),
+ defaultFunc = require("../functions/default"),
+ getDebugInfo = require("./debug-info");
+
+var Ruleset = function (selectors, rules, strictImports) {
+ this.selectors = selectors;
+ this.rules = rules;
+ this._lookups = {};
+ this.strictImports = strictImports;
+};
+Ruleset.prototype = new Node();
+Ruleset.prototype.type = "Ruleset";
+Ruleset.prototype.isRuleset = true;
+Ruleset.prototype.isRulesetLike = true;
+Ruleset.prototype.accept = function (visitor) {
+ if (this.paths) {
+ visitor.visitArray(this.paths, true);
+ } else if (this.selectors) {
+ this.selectors = visitor.visitArray(this.selectors);
+ }
+ if (this.rules && this.rules.length) {
+ this.rules = visitor.visitArray(this.rules);
+ }
+};
+Ruleset.prototype.eval = function (context) {
+ var thisSelectors = this.selectors, selectors,
+ selCnt, selector, i, hasOnePassingSelector = false;
+
+ if (thisSelectors && (selCnt = thisSelectors.length)) {
+ selectors = [];
+ defaultFunc.error({
+ type: "Syntax",
+ message: "it is currently only allowed in parametric mixin guards,"
+ });
+ for (i = 0; i < selCnt; i++) {
+ selector = thisSelectors[i].eval(context);
+ selectors.push(selector);
+ if (selector.evaldCondition) {
+ hasOnePassingSelector = true;
+ }
+ }
+ defaultFunc.reset();
+ } else {
+ hasOnePassingSelector = true;
+ }
+
+ var rules = this.rules ? this.rules.slice(0) : null,
+ ruleset = new Ruleset(selectors, rules, this.strictImports),
+ rule, subRule;
+
+ ruleset.originalRuleset = this;
+ ruleset.root = this.root;
+ ruleset.firstRoot = this.firstRoot;
+ ruleset.allowImports = this.allowImports;
+
+ if (this.debugInfo) {
+ ruleset.debugInfo = this.debugInfo;
+ }
+
+ if (!hasOnePassingSelector) {
+ rules.length = 0;
+ }
+
+ // push the current ruleset to the frames stack
+ var ctxFrames = context.frames;
+ ctxFrames.unshift(ruleset);
+
+ // currrent selectors
+ var ctxSelectors = context.selectors;
+ if (!ctxSelectors) {
+ context.selectors = ctxSelectors = [];
+ }
+ ctxSelectors.unshift(this.selectors);
+
+ // Evaluate imports
+ if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
+ ruleset.evalImports(context);
+ }
+
+ // Store the frames around mixin definitions,
+ // so they can be evaluated like closures when the time comes.
+ var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
+ for (i = 0; i < rsRuleCnt; i++) {
+ if (rsRules[i].evalFirst) {
+ rsRules[i] = rsRules[i].eval(context);
+ }
+ }
+
+ var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
+
+ // Evaluate mixin calls.
+ for (i = 0; i < rsRuleCnt; i++) {
+ if (rsRules[i].type === "MixinCall") {
+ /*jshint loopfunc:true */
+ rules = rsRules[i].eval(context).filter(function(r) {
+ if ((r instanceof Rule) && r.variable) {
+ // do not pollute the scope if the variable is
+ // already there. consider returning false here
+ // but we need a way to "return" variable from mixins
+ return !(ruleset.variable(r.name));
+ }
+ return true;
+ });
+ rsRules.splice.apply(rsRules, [i, 1].concat(rules));
+ rsRuleCnt += rules.length - 1;
+ i += rules.length - 1;
+ ruleset.resetCache();
+ } else if (rsRules[i].type === "RulesetCall") {
+ /*jshint loopfunc:true */
+ rules = rsRules[i].eval(context).rules.filter(function(r) {
+ if ((r instanceof Rule) && r.variable) {
+ // do not pollute the scope at all
+ return false;
+ }
+ return true;
+ });
+ rsRules.splice.apply(rsRules, [i, 1].concat(rules));
+ rsRuleCnt += rules.length - 1;
+ i += rules.length - 1;
+ ruleset.resetCache();
+ }
+ }
+
+ // Evaluate everything else
+ for (i = 0; i < rsRules.length; i++) {
+ rule = rsRules[i];
+ if (!rule.evalFirst) {
+ rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
+ }
+ }
+
+ // Evaluate everything else
+ for (i = 0; i < rsRules.length; i++) {
+ rule = rsRules[i];
+ // for rulesets, check if it is a css guard and can be removed
+ if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
+ // check if it can be folded in (e.g. & where)
+ if (rule.selectors[0].isJustParentSelector()) {
+ rsRules.splice(i--, 1);
+
+ for (var j = 0; j < rule.rules.length; j++) {
+ subRule = rule.rules[j];
+ if (!(subRule instanceof Rule) || !subRule.variable) {
+ rsRules.splice(++i, 0, subRule);
+ }
+ }
+ }
+ }
+ }
+
+ // Pop the stack
+ ctxFrames.shift();
+ ctxSelectors.shift();
+
+ if (context.mediaBlocks) {
+ for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) {
+ context.mediaBlocks[i].bubbleSelectors(selectors);
+ }
+ }
+
+ return ruleset;
+};
+Ruleset.prototype.evalImports = function(context) {
+ var rules = this.rules, i, importRules;
+ if (!rules) { return; }
+
+ for (i = 0; i < rules.length; i++) {
+ if (rules[i].type === "Import") {
+ importRules = rules[i].eval(context);
+ if (importRules && importRules.length) {
+ rules.splice.apply(rules, [i, 1].concat(importRules));
+ i+= importRules.length - 1;
+ } else {
+ rules.splice(i, 1, importRules);
+ }
+ this.resetCache();
+ }
+ }
+};
+Ruleset.prototype.makeImportant = function() {
+ var result = new Ruleset(this.selectors, this.rules.map(function (r) {
+ if (r.makeImportant) {
+ return r.makeImportant();
+ } else {
+ return r;
+ }
+ }), this.strictImports);
+
+ return result;
+};
+Ruleset.prototype.matchArgs = function (args) {
+ return !args || args.length === 0;
+};
+// lets you call a css selector with a guard
+Ruleset.prototype.matchCondition = function (args, context) {
+ var lastSelector = this.selectors[this.selectors.length - 1];
+ if (!lastSelector.evaldCondition) {
+ return false;
+ }
+ if (lastSelector.condition &&
+ !lastSelector.condition.eval(
+ new contexts.Eval(context,
+ context.frames))) {
+ return false;
+ }
+ return true;
+};
+Ruleset.prototype.resetCache = function () {
+ this._rulesets = null;
+ this._variables = null;
+ this._lookups = {};
+};
+Ruleset.prototype.variables = function () {
+ if (!this._variables) {
+ this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
+ if (r instanceof Rule && r.variable === true) {
+ hash[r.name] = r;
+ }
+ // when evaluating variables in an import statement, imports have not been eval'd
+ // so we need to go inside import statements.
+ // guard against root being a string (in the case of inlined less)
+ if (r.type === "Import" && r.root && r.root.variables) {
+ var vars = r.root.variables();
+ for (var name in vars) {
+ if (vars.hasOwnProperty(name)) {
+ hash[name] = vars[name];
+ }
+ }
+ }
+ return hash;
+ }, {});
+ }
+ return this._variables;
+};
+Ruleset.prototype.variable = function (name) {
+ return this.variables()[name];
+};
+Ruleset.prototype.rulesets = function () {
+ if (!this.rules) { return null; }
+
+ var filtRules = [], rules = this.rules, cnt = rules.length,
+ i, rule;
+
+ for (i = 0; i < cnt; i++) {
+ rule = rules[i];
+ if (rule.isRuleset) {
+ filtRules.push(rule);
+ }
+ }
+
+ return filtRules;
+};
+Ruleset.prototype.prependRule = function (rule) {
+ var rules = this.rules;
+ if (rules) {
+ rules.unshift(rule);
+ } else {
+ this.rules = [ rule ];
+ }
+};
+Ruleset.prototype.find = function (selector, self, filter) {
+ self = self || this;
+ var rules = [], match, foundMixins,
+ key = selector.toCSS();
+
+ if (key in this._lookups) { return this._lookups[key]; }
+
+ this.rulesets().forEach(function (rule) {
+ if (rule !== self) {
+ for (var j = 0; j < rule.selectors.length; j++) {
+ match = selector.match(rule.selectors[j]);
+ if (match) {
+ if (selector.elements.length > match) {
+ if (!filter || filter(rule)) {
+ foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter);
+ for (var i = 0; i < foundMixins.length; ++i) {
+ foundMixins[i].path.push(rule);
+ }
+ Array.prototype.push.apply(rules, foundMixins);
+ }
+ } else {
+ rules.push({ rule: rule, path: []});
+ }
+ break;
+ }
+ }
+ }
+ });
+ this._lookups[key] = rules;
+ return rules;
+};
+Ruleset.prototype.genCSS = function (context, output) {
+ var i, j,
+ charsetRuleNodes = [],
+ ruleNodes = [],
+ rulesetNodes = [],
+ rulesetNodeCnt,
+ debugInfo, // Line number debugging
+ rule,
+ path;
+
+ context.tabLevel = (context.tabLevel || 0);
+
+ if (!this.root) {
+ context.tabLevel++;
+ }
+
+ var tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(" "),
+ tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "),
+ sep;
+
+ function isRulesetLikeNode(rule, root) {
+ // if it has nested rules, then it should be treated like a ruleset
+ // medias and comments do not have nested rules, but should be treated like rulesets anyway
+ // some directives and anonymous nodes are ruleset like, others are not
+ if (typeof rule.isRulesetLike === "boolean") {
+ return rule.isRulesetLike;
+ } else if (typeof rule.isRulesetLike === "function") {
+ return rule.isRulesetLike(root);
+ }
+
+ //anything else is assumed to be a rule
+ return false;
+ }
+
+ for (i = 0; i < this.rules.length; i++) {
+ rule = this.rules[i];
+ if (isRulesetLikeNode(rule, this.root)) {
+ rulesetNodes.push(rule);
+ } else {
+ //charsets should float on top of everything
+ if (rule.isCharset && rule.isCharset()) {
+ charsetRuleNodes.push(rule);
+ } else {
+ ruleNodes.push(rule);
+ }
+ }
+ }
+ ruleNodes = charsetRuleNodes.concat(ruleNodes);
+
+ // If this is the root node, we don't render
+ // a selector, or {}.
+ if (!this.root) {
+ debugInfo = getDebugInfo(context, this, tabSetStr);
+
+ if (debugInfo) {
+ output.add(debugInfo);
+ output.add(tabSetStr);
+ }
+
+ var paths = this.paths, pathCnt = paths.length,
+ pathSubCnt;
+
+ sep = context.compress ? ',' : (',\n' + tabSetStr);
+
+ for (i = 0; i < pathCnt; i++) {
+ path = paths[i];
+ if (!(pathSubCnt = path.length)) { continue; }
+ if (i > 0) { output.add(sep); }
+
+ context.firstSelector = true;
+ path[0].genCSS(context, output);
+
+ context.firstSelector = false;
+ for (j = 1; j < pathSubCnt; j++) {
+ path[j].genCSS(context, output);
+ }
+ }
+
+ output.add((context.compress ? '{' : ' {\n') + tabRuleStr);
+ }
+
+ // Compile rules and rulesets
+ for (i = 0; i < ruleNodes.length; i++) {
+ rule = ruleNodes[i];
+
+ // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
+ // In this instance we do not know whether it is the last property
+ if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) {
+ context.lastRule = true;
+ }
+
+ if (rule.genCSS) {
+ rule.genCSS(context, output);
+ } else if (rule.value) {
+ output.add(rule.value.toString());
+ }
+
+ if (!context.lastRule) {
+ output.add(context.compress ? '' : ('\n' + tabRuleStr));
+ } else {
+ context.lastRule = false;
+ }
+ }
+
+ if (!this.root) {
+ output.add((context.compress ? '}' : '\n' + tabSetStr + '}'));
+ context.tabLevel--;
+ }
+
+ sep = (context.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr);
+ rulesetNodeCnt = rulesetNodes.length;
+ if (rulesetNodeCnt) {
+ if (ruleNodes.length && sep) { output.add(sep); }
+ rulesetNodes[0].genCSS(context, output);
+ for (i = 1; i < rulesetNodeCnt; i++) {
+ if (sep) { output.add(sep); }
+ rulesetNodes[i].genCSS(context, output);
+ }
+ }
+
+ if (!output.isEmpty() && !context.compress && this.firstRoot) {
+ output.add('\n');
+ }
+};
+Ruleset.prototype.markReferenced = function () {
+ var s;
+ if (this.selectors) {
+ for (s = 0; s < this.selectors.length; s++) {
+ this.selectors[s].markReferenced();
+ }
+ }
+
+ if (this.rules) {
+ for (s = 0; s < this.rules.length; s++) {
+ if (this.rules[s].markReferenced) {
+ this.rules[s].markReferenced();
+ }
+ }
+ }
+};
+Ruleset.prototype.getIsReferenced = function() {
+ var i, j, path, selector;
+
+ if (this.paths) {
+ for (i = 0; i < this.paths.length; i++) {
+ path = this.paths[i];
+ for (j = 0; j < path.length; j++) {
+ if (path[j].getIsReferenced && path[j].getIsReferenced()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ if (this.selectors) {
+ for (i = 0; i < this.selectors.length; i++) {
+ selector = this.selectors[i];
+ if (selector.getIsReferenced && selector.getIsReferenced()) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
+ for (var s = 0; s < selectors.length; s++) {
+ this.joinSelector(paths, context, selectors[s]);
+ }
+};
+
+Ruleset.prototype.joinSelector = function (paths, context, selector) {
+
+ function createParenthesis(elementsToPak, originalElement) {
+ var replacementParen, j;
+ if (elementsToPak.length === 0) {
+ replacementParen = new Paren(elementsToPak[0]);
+ } else {
+ var insideParent = [];
+ for (j = 0; j < elementsToPak.length; j++) {
+ insideParent.push(new Element(null, elementsToPak[j], originalElement.index, originalElement.currentFileInfo));
+ }
+ replacementParen = new Paren(new Selector(insideParent));
+ }
+ return replacementParen;
+ }
+
+ function createSelector(containedElement, originalElement) {
+ var element, selector;
+ element = new Element(null, containedElement, originalElement.index, originalElement.currentFileInfo);
+ selector = new Selector([element]);
+ return selector;
+ }
+
+ // replace all parent selectors inside `inSelector` by content of `context` array
+ // resulting selectors are returned inside `paths` array
+ // returns true if `inSelector` contained at least one parent selector
+ function replaceParentSelector(paths, context, inSelector) {
+ // The paths are [[Selector]]
+ // The first list is a list of comma separated selectors
+ // The inner list is a list of inheritance separated selectors
+ // e.g.
+ // .a, .b {
+ // .c {
+ // }
+ // }
+ // == [[.a] [.c]] [[.b] [.c]]
+ //
+ var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector;
+ function findNestedSelector(element) {
+ var maybeSelector;
+ if (element.value.type !== 'Paren') {
+ return null;
+ }
+
+ maybeSelector = element.value.value;
+ if (maybeSelector.type !== 'Selector') {
+ return null;
+ }
+
+ return maybeSelector;
+ }
+
+ // the elements from the current selector so far
+ currentElements = [];
+ // the current list of new selectors to add to the path.
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
+ // by the parents
+ newSelectors = [
+ []
+ ];
+
+ for (i = 0; i < inSelector.elements.length; i++) {
+ el = inSelector.elements[i];
+ // non parent reference elements just get added
+ if (el.value !== "&") {
+ var nestedSelector = findNestedSelector(el);
+ if (nestedSelector != null) {
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ var nestedPaths = [], replaced, replacedNewSelectors = [];
+ replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
+ hadParentSelector = hadParentSelector || replaced;
+ //the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
+ for (k = 0; k < nestedPaths.length; k++) {
+ var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
+ addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
+ }
+ newSelectors = replacedNewSelectors;
+ currentElements = [];
+
+ } else {
+ currentElements.push(el);
+ }
+
+ } else {
+ hadParentSelector = true;
+ // the new list of selectors to add
+ selectorsMultiplied = [];
+
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ // loop through our current selectors
+ for (j = 0; j < newSelectors.length; j++) {
+ sel = newSelectors[j];
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
+ // whether there are parents or not
+ if (context.length === 0) {
+ // the combinator used on el should now be applied to the next element instead so that
+ // it is not lost
+ if (sel.length > 0) {
+ sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo));
+ }
+ selectorsMultiplied.push(sel);
+ }
+ else {
+ // and the parent selectors
+ for (k = 0; k < context.length; k++) {
+ // We need to put the current selectors
+ // then join the last selector's elements on to the parents selectors
+ var newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
+ // add that to our new set of selectors
+ selectorsMultiplied.push(newSelectorPath);
+ }
+ }
+ }
+
+ // our new selectors has been multiplied, so reset the state
+ newSelectors = selectorsMultiplied;
+ currentElements = [];
+ }
+ }
+
+ // if we have any elements left over (e.g. .a& .b == .b)
+ // add them on to all the current selectors
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ for (i = 0; i < newSelectors.length; i++) {
+ length = newSelectors[i].length;
+ if (length > 0) {
+ paths.push(newSelectors[i]);
+ lastSelector = newSelectors[i][length - 1];
+ newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
+ }
+ }
+
+ return hadParentSelector;
+ }
+
+ // joins selector path from `beginningPath` with selector path in `addPath`
+ // `replacedElement` contains element that is being replaced by `addPath`
+ // returns concatenated path
+ function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
+ var newSelectorPath, lastSelector, newJoinedSelector;
+ // our new selector path
+ newSelectorPath = [];
+
+ //construct the joined selector - if & is the first thing this will be empty,
+ // if not newJoinedSelector will be the last set of elements in the selector
+ if (beginningPath.length > 0) {
+ newSelectorPath = beginningPath.slice(0);
+ lastSelector = newSelectorPath.pop();
+ newJoinedSelector = originalSelector.createDerived(lastSelector.elements.slice(0));
+ }
+ else {
+ newJoinedSelector = originalSelector.createDerived([]);
+ }
+
+ if (addPath.length > 0) {
+ // /deep/ is a combinator that is valid without anything in front of it
+ // so if the & does not have a combinator that is "" or " " then
+ // and there is a combinator on the parent, then grab that.
+ // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
+ var combinator = replacedElement.combinator, parentEl = addPath[0].elements[0];
+ if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
+ combinator = parentEl.combinator;
+ }
+ // join the elements so far with the first part of the parent
+ newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement.index, replacedElement.currentFileInfo));
+ newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
+ }
+
+ // now add the joined selector - but only if it is not empty
+ if (newJoinedSelector.elements.length !== 0) {
+ newSelectorPath.push(newJoinedSelector);
+ }
+
+ //put together the parent selectors after the join (e.g. the rest of the parent)
+ if (addPath.length > 1) {
+ newSelectorPath = newSelectorPath.concat(addPath.slice(1));
+ }
+ return newSelectorPath;
+ }
+
+ // joins selector path from `beginningPath` with every selector path in `addPaths` array
+ // `replacedElement` contains element that is being replaced by `addPath`
+ // returns array with all concatenated paths
+ function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector, result) {
+ var j;
+ for (j = 0; j < beginningPath.length; j++) {
+ var newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
+ result.push(newSelectorPath);
+ }
+ return result;
+ }
+
+ function mergeElementsOnToSelectors(elements, selectors) {
+ var i, sel;
+
+ if (elements.length === 0) {
+ return ;
+ }
+ if (selectors.length === 0) {
+ selectors.push([ new Selector(elements) ]);
+ return;
+ }
+
+ for (i = 0; i < selectors.length; i++) {
+ sel = selectors[i];
+
+ // if the previous thing in sel is a parent this needs to join on to it
+ if (sel.length > 0) {
+ sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
+ }
+ else {
+ sel.push(new Selector(elements));
+ }
+ }
+ }
+
+ // joinSelector code follows
+ var i, newPaths, hadParentSelector;
+
+ newPaths = [];
+ hadParentSelector = replaceParentSelector(newPaths, context, selector);
+
+ if (!hadParentSelector) {
+ if (context.length > 0) {
+ newPaths = [];
+ for (i = 0; i < context.length; i++) {
+ newPaths.push(context[i].concat(selector));
+ }
+ }
+ else {
+ newPaths = [[selector]];
+ }
+ }
+
+ for (i = 0; i < newPaths.length; i++) {
+ paths.push(newPaths[i]);
+ }
+
+};
+module.exports = Ruleset;
+
+},{"../contexts":10,"../functions/default":19,"./debug-info":51,"./element":55,"./node":67,"./paren":69,"./rule":71,"./selector":74}],74:[function(require,module,exports){
+var Node = require("./node");
+
+var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) {
+ this.elements = elements;
+ this.extendList = extendList;
+ this.condition = condition;
+ this.currentFileInfo = currentFileInfo || {};
+ this.isReferenced = isReferenced;
+ if (!condition) {
+ this.evaldCondition = true;
+ }
+};
+Selector.prototype = new Node();
+Selector.prototype.type = "Selector";
+Selector.prototype.accept = function (visitor) {
+ if (this.elements) {
+ this.elements = visitor.visitArray(this.elements);
+ }
+ if (this.extendList) {
+ this.extendList = visitor.visitArray(this.extendList);
+ }
+ if (this.condition) {
+ this.condition = visitor.visit(this.condition);
+ }
+};
+Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
+ evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
+ var newSelector = new Selector(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
+ newSelector.evaldCondition = evaldCondition;
+ newSelector.mediaEmpty = this.mediaEmpty;
+ return newSelector;
+};
+Selector.prototype.match = function (other) {
+ var elements = this.elements,
+ len = elements.length,
+ olen, i;
+
+ other.CacheElements();
+
+ olen = other._elements.length;
+ if (olen === 0 || len < olen) {
+ return 0;
+ } else {
+ for (i = 0; i < olen; i++) {
+ if (elements[i].value !== other._elements[i]) {
+ return 0;
+ }
+ }
+ }
+
+ return olen; // return number of matched elements
+};
+Selector.prototype.CacheElements = function() {
+ if (this._elements) {
+ return;
+ }
+
+ var elements = this.elements.map( function(v) {
+ return v.combinator.value + (v.value.value || v.value);
+ }).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
+
+ if (elements) {
+ if (elements[0] === "&") {
+ elements.shift();
+ }
+ } else {
+ elements = [];
+ }
+
+ this._elements = elements;
+};
+Selector.prototype.isJustParentSelector = function() {
+ return !this.mediaEmpty &&
+ this.elements.length === 1 &&
+ this.elements[0].value === '&' &&
+ (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
+};
+Selector.prototype.eval = function (context) {
+ var evaldCondition = this.condition && this.condition.eval(context),
+ elements = this.elements, extendList = this.extendList;
+
+ elements = elements && elements.map(function (e) { return e.eval(context); });
+ extendList = extendList && extendList.map(function(extend) { return extend.eval(context); });
+
+ return this.createDerived(elements, extendList, evaldCondition);
+};
+Selector.prototype.genCSS = function (context, output) {
+ var i, element;
+ if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") {
+ output.add(' ', this.currentFileInfo, this.index);
+ }
+ if (!this._css) {
+ //TODO caching? speed comparison?
+ for (i = 0; i < this.elements.length; i++) {
+ element = this.elements[i];
+ element.genCSS(context, output);
+ }
+ }
+};
+Selector.prototype.markReferenced = function () {
+ this.isReferenced = true;
+};
+Selector.prototype.getIsReferenced = function() {
+ return !this.currentFileInfo.reference || this.isReferenced;
+};
+Selector.prototype.getIsOutput = function() {
+ return this.evaldCondition;
+};
+module.exports = Selector;
+
+},{"./node":67}],75:[function(require,module,exports){
+var Node = require("./node");
+
+var UnicodeDescriptor = function (value) {
+ this.value = value;
+};
+UnicodeDescriptor.prototype = new Node();
+UnicodeDescriptor.prototype.type = "UnicodeDescriptor";
+
+module.exports = UnicodeDescriptor;
+
+},{"./node":67}],76:[function(require,module,exports){
+var Node = require("./node"),
+ unitConversions = require("../data/unit-conversions");
+
+var Unit = function (numerator, denominator, backupUnit) {
+ this.numerator = numerator ? numerator.slice(0).sort() : [];
+ this.denominator = denominator ? denominator.slice(0).sort() : [];
+ if (backupUnit) {
+ this.backupUnit = backupUnit;
+ } else if (numerator && numerator.length) {
+ this.backupUnit = numerator[0];
+ }
+};
+
+Unit.prototype = new Node();
+Unit.prototype.type = "Unit";
+Unit.prototype.clone = function () {
+ return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
+};
+Unit.prototype.genCSS = function (context, output) {
+ // Dimension checks the unit is singular and throws an error if in strict math mode.
+ var strictUnits = context && context.strictUnits;
+ if (this.numerator.length === 1) {
+ output.add(this.numerator[0]); // the ideal situation
+ } else if (!strictUnits && this.backupUnit) {
+ output.add(this.backupUnit);
+ } else if (!strictUnits && this.denominator.length) {
+ output.add(this.denominator[0]);
+ }
+};
+Unit.prototype.toString = function () {
+ var i, returnStr = this.numerator.join("*");
+ for (i = 0; i < this.denominator.length; i++) {
+ returnStr += "/" + this.denominator[i];
+ }
+ return returnStr;
+};
+Unit.prototype.compare = function (other) {
+ return this.is(other.toString()) ? 0 : undefined;
+};
+Unit.prototype.is = function (unitString) {
+ return this.toString().toUpperCase() === unitString.toUpperCase();
+};
+Unit.prototype.isLength = function () {
+ return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
+};
+Unit.prototype.isEmpty = function () {
+ return this.numerator.length === 0 && this.denominator.length === 0;
+};
+Unit.prototype.isSingular = function() {
+ return this.numerator.length <= 1 && this.denominator.length === 0;
+};
+Unit.prototype.map = function(callback) {
+ var i;
+
+ for (i = 0; i < this.numerator.length; i++) {
+ this.numerator[i] = callback(this.numerator[i], false);
+ }
+
+ for (i = 0; i < this.denominator.length; i++) {
+ this.denominator[i] = callback(this.denominator[i], true);
+ }
+};
+Unit.prototype.usedUnits = function() {
+ var group, result = {}, mapUnit;
+
+ mapUnit = function (atomicUnit) {
+ /*jshint loopfunc:true */
+ if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
+ result[groupName] = atomicUnit;
+ }
+
+ return atomicUnit;
+ };
+
+ for (var groupName in unitConversions) {
+ if (unitConversions.hasOwnProperty(groupName)) {
+ group = unitConversions[groupName];
+
+ this.map(mapUnit);
+ }
+ }
+
+ return result;
+};
+Unit.prototype.cancel = function () {
+ var counter = {}, atomicUnit, i;
+
+ for (i = 0; i < this.numerator.length; i++) {
+ atomicUnit = this.numerator[i];
+ counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
+ }
+
+ for (i = 0; i < this.denominator.length; i++) {
+ atomicUnit = this.denominator[i];
+ counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
+ }
+
+ this.numerator = [];
+ this.denominator = [];
+
+ for (atomicUnit in counter) {
+ if (counter.hasOwnProperty(atomicUnit)) {
+ var count = counter[atomicUnit];
+
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ this.numerator.push(atomicUnit);
+ }
+ } else if (count < 0) {
+ for (i = 0; i < -count; i++) {
+ this.denominator.push(atomicUnit);
+ }
+ }
+ }
+ }
+
+ this.numerator.sort();
+ this.denominator.sort();
+};
+module.exports = Unit;
+
+},{"../data/unit-conversions":13,"./node":67}],77:[function(require,module,exports){
+var Node = require("./node");
+
+var URL = function (val, index, currentFileInfo, isEvald) {
+ this.value = val;
+ this.currentFileInfo = currentFileInfo;
+ this.index = index;
+ this.isEvald = isEvald;
+};
+URL.prototype = new Node();
+URL.prototype.type = "Url";
+URL.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+URL.prototype.genCSS = function (context, output) {
+ output.add("url(");
+ this.value.genCSS(context, output);
+ output.add(")");
+};
+URL.prototype.eval = function (context) {
+ var val = this.value.eval(context),
+ rootpath;
+
+ if (!this.isEvald) {
+ // Add the base path if the URL is relative
+ rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
+ if (rootpath &&
+ typeof val.value === "string" &&
+ context.isPathRelative(val.value)) {
+
+ if (!val.quote) {
+ rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\" + match; });
+ }
+ val.value = rootpath + val.value;
+ }
+
+ val.value = context.normalizePath(val.value);
+
+ // Add url args if enabled
+ if (context.urlArgs) {
+ if (!val.value.match(/^\s*data:/)) {
+ var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
+ var urlArgs = delimiter + context.urlArgs;
+ if (val.value.indexOf('#') !== -1) {
+ val.value = val.value.replace('#', urlArgs + '#');
+ } else {
+ val.value += urlArgs;
+ }
+ }
+ }
+ }
+
+ return new URL(val, this.index, this.currentFileInfo, true);
+};
+module.exports = URL;
+
+},{"./node":67}],78:[function(require,module,exports){
+var Node = require("./node");
+
+var Value = function (value) {
+ this.value = value;
+ if (!value) {
+ throw new Error("Value requires an array argument");
+ }
+};
+Value.prototype = new Node();
+Value.prototype.type = "Value";
+Value.prototype.accept = function (visitor) {
+ if (this.value) {
+ this.value = visitor.visitArray(this.value);
+ }
+};
+Value.prototype.eval = function (context) {
+ if (this.value.length === 1) {
+ return this.value[0].eval(context);
+ } else {
+ return new Value(this.value.map(function (v) {
+ return v.eval(context);
+ }));
+ }
+};
+Value.prototype.genCSS = function (context, output) {
+ var i;
+ for (i = 0; i < this.value.length; i++) {
+ this.value[i].genCSS(context, output);
+ if (i + 1 < this.value.length) {
+ output.add((context && context.compress) ? ',' : ', ');
+ }
+ }
+};
+module.exports = Value;
+
+},{"./node":67}],79:[function(require,module,exports){
+var Node = require("./node");
+
+var Variable = function (name, index, currentFileInfo) {
+ this.name = name;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo || {};
+};
+Variable.prototype = new Node();
+Variable.prototype.type = "Variable";
+Variable.prototype.eval = function (context) {
+ var variable, name = this.name;
+
+ if (name.indexOf('@@') === 0) {
+ name = '@' + new Variable(name.slice(1), this.index, this.currentFileInfo).eval(context).value;
+ }
+
+ if (this.evaluating) {
+ throw { type: 'Name',
+ message: "Recursive variable definition for " + name,
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+
+ this.evaluating = true;
+
+ variable = this.find(context.frames, function (frame) {
+ var v = frame.variable(name);
+ if (v) {
+ if (v.important) {
+ var importantScope = context.importantScope[context.importantScope.length - 1];
+ importantScope.important = v.important;
+ }
+ return v.value.eval(context);
+ }
+ });
+ if (variable) {
+ this.evaluating = false;
+ return variable;
+ } else {
+ throw { type: 'Name',
+ message: "variable " + name + " is undefined",
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+};
+Variable.prototype.find = function (obj, fun) {
+ for (var i = 0, r; i < obj.length; i++) {
+ r = fun.call(obj, obj[i]);
+ if (r) { return r; }
+ }
+ return null;
+};
+module.exports = Variable;
+
+},{"./node":67}],80:[function(require,module,exports){
+module.exports = {
+ getLocation: function(index, inputStream) {
+ var n = index + 1,
+ line = null,
+ column = -1;
+
+ while (--n >= 0 && inputStream.charAt(n) !== '\n') {
+ column++;
+ }
+
+ if (typeof index === 'number') {
+ line = (inputStream.slice(0, index).match(/\n/g) || "").length;
+ }
+
+ return {
+ line: line,
+ column: column
+ };
+ }
+};
+
+},{}],81:[function(require,module,exports){
+var tree = require("../tree"),
+ Visitor = require("./visitor"),
+ logger = require("../logger");
+
+/*jshint loopfunc:true */
+
+var ExtendFinderVisitor = function() {
+ this._visitor = new Visitor(this);
+ this.contexts = [];
+ this.allExtendsStack = [[]];
+};
+
+ExtendFinderVisitor.prototype = {
+ run: function (root) {
+ root = this._visitor.visit(root);
+ root.allExtends = this.allExtendsStack[0];
+ return root;
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitRuleset: function (rulesetNode, visitArgs) {
+ if (rulesetNode.root) {
+ return;
+ }
+
+ var i, j, extend, allSelectorsExtendList = [], extendList;
+
+ // get &:extend(.a); rules which apply to all selectors in this ruleset
+ var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0;
+ for (i = 0; i < ruleCnt; i++) {
+ if (rulesetNode.rules[i] instanceof tree.Extend) {
+ allSelectorsExtendList.push(rules[i]);
+ rulesetNode.extendOnEveryPath = true;
+ }
+ }
+
+ // now find every selector and apply the extends that apply to all extends
+ // and the ones which apply to an individual extend
+ var paths = rulesetNode.paths;
+ for (i = 0; i < paths.length; i++) {
+ var selectorPath = paths[i],
+ selector = selectorPath[selectorPath.length - 1],
+ selExtendList = selector.extendList;
+
+ extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList)
+ : allSelectorsExtendList;
+
+ if (extendList) {
+ extendList = extendList.map(function(allSelectorsExtend) {
+ return allSelectorsExtend.clone();
+ });
+ }
+
+ for (j = 0; j < extendList.length; j++) {
+ this.foundExtends = true;
+ extend = extendList[j];
+ extend.findSelfSelectors(selectorPath);
+ extend.ruleset = rulesetNode;
+ if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
+ this.allExtendsStack[this.allExtendsStack.length - 1].push(extend);
+ }
+ }
+
+ this.contexts.push(rulesetNode.selectors);
+ },
+ visitRulesetOut: function (rulesetNode) {
+ if (!rulesetNode.root) {
+ this.contexts.length = this.contexts.length - 1;
+ }
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ mediaNode.allExtends = [];
+ this.allExtendsStack.push(mediaNode.allExtends);
+ },
+ visitMediaOut: function (mediaNode) {
+ this.allExtendsStack.length = this.allExtendsStack.length - 1;
+ },
+ visitDirective: function (directiveNode, visitArgs) {
+ directiveNode.allExtends = [];
+ this.allExtendsStack.push(directiveNode.allExtends);
+ },
+ visitDirectiveOut: function (directiveNode) {
+ this.allExtendsStack.length = this.allExtendsStack.length - 1;
+ }
+};
+
+var ProcessExtendsVisitor = function() {
+ this._visitor = new Visitor(this);
+};
+
+ProcessExtendsVisitor.prototype = {
+ run: function(root) {
+ var extendFinder = new ExtendFinderVisitor();
+ this.extendIndicies = {};
+ extendFinder.run(root);
+ if (!extendFinder.foundExtends) { return root; }
+ root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
+ this.allExtendsStack = [root.allExtends];
+ var newRoot = this._visitor.visit(root);
+ this.checkExtendsForNonMatched(root.allExtends);
+ return newRoot;
+ },
+ checkExtendsForNonMatched: function(extendList) {
+ var indicies = this.extendIndicies;
+ extendList.filter(function(extend) {
+ return !extend.hasFoundMatches && extend.parent_ids.length == 1;
+ }).forEach(function(extend) {
+ var selector = "_unknown_";
+ try {
+ selector = extend.selector.toCSS({});
+ }
+ catch(_) {}
+
+ if (!indicies[extend.index + ' ' + selector]) {
+ indicies[extend.index + ' ' + selector] = true;
+ logger.warn("extend '" + selector + "' has no matches");
+ }
+ });
+ },
+ doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
+ //
+ // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering
+ // and pasting the selector we would do normally, but we are also adding an extend with the same target selector
+ // this means this new extend can then go and alter other extends
+ //
+ // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
+ // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already
+ // processed if we look at each selector at a time, as is done in visitRuleset
+
+ var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath,
+ extend, targetExtend, newExtend;
+
+ iterationCount = iterationCount || 0;
+
+ //loop through comparing every extend with every target extend.
+ // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
+ // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
+ // and the second is the target.
+ // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
+ // case when processing media queries
+ for (extendIndex = 0; extendIndex < extendsList.length; extendIndex++) {
+ for (targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++) {
+
+ extend = extendsList[extendIndex];
+ targetExtend = extendsListTarget[targetExtendIndex];
+
+ // look for circular references
+ if ( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ) { continue; }
+
+ // find a match in the target extends self selector (the bit before :extend)
+ selectorPath = [targetExtend.selfSelectors[0]];
+ matches = extendVisitor.findMatch(extend, selectorPath);
+
+ if (matches.length) {
+
+ extend.hasFoundMatches = true;
+
+ // we found a match, so for each self selector..
+ extend.selfSelectors.forEach(function(selfSelector) {
+
+ // process the extend as usual
+ newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector);
+
+ // but now we create a new extend from it
+ newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0);
+ newExtend.selfSelectors = newSelector;
+
+ // add the extend onto the list of extends for that selector
+ newSelector[newSelector.length - 1].extendList = [newExtend];
+
+ // record that we need to add it.
+ extendsToAdd.push(newExtend);
+ newExtend.ruleset = targetExtend.ruleset;
+
+ //remember its parents for circular references
+ newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids);
+
+ // only process the selector once.. if we have :extend(.a,.b) then multiple
+ // extends will look at the same selector path, so when extending
+ // we know that any others will be duplicates in terms of what is added to the css
+ if (targetExtend.firstExtendOnThisSelectorPath) {
+ newExtend.firstExtendOnThisSelectorPath = true;
+ targetExtend.ruleset.paths.push(newSelector);
+ }
+ });
+ }
+ }
+ }
+
+ if (extendsToAdd.length) {
+ // try to detect circular references to stop a stack overflow.
+ // may no longer be needed.
+ this.extendChainCount++;
+ if (iterationCount > 100) {
+ var selectorOne = "{unable to calculate}";
+ var selectorTwo = "{unable to calculate}";
+ try
+ {
+ selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
+ selectorTwo = extendsToAdd[0].selector.toCSS();
+ }
+ catch(e) {}
+ throw { message: "extend circular reference detected. One of the circular extends is currently:" +
+ selectorOne + ":extend(" + selectorTwo + ")"};
+ }
+
+ // now process the new extends on the existing rules so that we can handle a extending b extending c extending
+ // d extending e...
+ return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount + 1));
+ } else {
+ return extendsToAdd;
+ }
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitSelector: function (selectorNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitRuleset: function (rulesetNode, visitArgs) {
+ if (rulesetNode.root) {
+ return;
+ }
+ var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length - 1],
+ selectorsToAdd = [], extendVisitor = this, selectorPath;
+
+ // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
+
+ for (extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
+ for (pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
+ selectorPath = rulesetNode.paths[pathIndex];
+
+ // extending extends happens initially, before the main pass
+ if (rulesetNode.extendOnEveryPath) { continue; }
+ var extendList = selectorPath[selectorPath.length - 1].extendList;
+ if (extendList && extendList.length) { continue; }
+
+ matches = this.findMatch(allExtends[extendIndex], selectorPath);
+
+ if (matches.length) {
+ allExtends[extendIndex].hasFoundMatches = true;
+
+ allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
+ selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
+ });
+ }
+ }
+ }
+ rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
+ },
+ findMatch: function (extend, haystackSelectorPath) {
+ //
+ // look through the haystack selector path to try and find the needle - extend.selector
+ // returns an array of selector matches that can then be replaced
+ //
+ var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement,
+ targetCombinator, i,
+ extendVisitor = this,
+ needleElements = extend.selector.elements,
+ potentialMatches = [], potentialMatch, matches = [];
+
+ // loop through the haystack elements
+ for (haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
+ hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
+
+ for (hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
+
+ haystackElement = hackstackSelector.elements[hackstackElementIndex];
+
+ // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
+ if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
+ potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0,
+ initialCombinator: haystackElement.combinator});
+ }
+
+ for (i = 0; i < potentialMatches.length; i++) {
+ potentialMatch = potentialMatches[i];
+
+ // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
+ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to
+ // work out what the resulting combinator will be
+ targetCombinator = haystackElement.combinator.value;
+ if (targetCombinator === '' && hackstackElementIndex === 0) {
+ targetCombinator = ' ';
+ }
+
+ // if we don't match, null our match to indicate failure
+ if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) ||
+ (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
+ potentialMatch = null;
+ } else {
+ potentialMatch.matched++;
+ }
+
+ // if we are still valid and have finished, test whether we have elements after and whether these are allowed
+ if (potentialMatch) {
+ potentialMatch.finished = potentialMatch.matched === needleElements.length;
+ if (potentialMatch.finished &&
+ (!extend.allowAfter &&
+ (hackstackElementIndex + 1 < hackstackSelector.elements.length || haystackSelectorIndex + 1 < haystackSelectorPath.length))) {
+ potentialMatch = null;
+ }
+ }
+ // if null we remove, if not, we are still valid, so either push as a valid match or continue
+ if (potentialMatch) {
+ if (potentialMatch.finished) {
+ potentialMatch.length = needleElements.length;
+ potentialMatch.endPathIndex = haystackSelectorIndex;
+ potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
+ potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
+ matches.push(potentialMatch);
+ }
+ } else {
+ potentialMatches.splice(i, 1);
+ i--;
+ }
+ }
+ }
+ }
+ return matches;
+ },
+ isElementValuesEqual: function(elementValue1, elementValue2) {
+ if (typeof elementValue1 === "string" || typeof elementValue2 === "string") {
+ return elementValue1 === elementValue2;
+ }
+ if (elementValue1 instanceof tree.Attribute) {
+ if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) {
+ return false;
+ }
+ if (!elementValue1.value || !elementValue2.value) {
+ if (elementValue1.value || elementValue2.value) {
+ return false;
+ }
+ return true;
+ }
+ elementValue1 = elementValue1.value.value || elementValue1.value;
+ elementValue2 = elementValue2.value.value || elementValue2.value;
+ return elementValue1 === elementValue2;
+ }
+ elementValue1 = elementValue1.value;
+ elementValue2 = elementValue2.value;
+ if (elementValue1 instanceof tree.Selector) {
+ if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
+ return false;
+ }
+ for (var i = 0; i < elementValue1.elements.length; i++) {
+ if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
+ if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
+ return false;
+ }
+ }
+ if (!this.isElementValuesEqual(elementValue1.elements[i].value, elementValue2.elements[i].value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ },
+ extendSelector:function (matches, selectorPath, replacementSelector) {
+
+ //for a set of matches, replace each match with the replacement selector
+
+ var currentSelectorPathIndex = 0,
+ currentSelectorPathElementIndex = 0,
+ path = [],
+ matchIndex,
+ selector,
+ firstElement,
+ match,
+ newElements;
+
+ for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
+ match = matches[matchIndex];
+ selector = selectorPath[match.pathIndex];
+ firstElement = new tree.Element(
+ match.initialCombinator,
+ replacementSelector.elements[0].value,
+ replacementSelector.elements[0].index,
+ replacementSelector.elements[0].currentFileInfo
+ );
+
+ if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
+ path[path.length - 1].elements = path[path.length - 1]
+ .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
+ currentSelectorPathElementIndex = 0;
+ currentSelectorPathIndex++;
+ }
+
+ newElements = selector.elements
+ .slice(currentSelectorPathElementIndex, match.index)
+ .concat([firstElement])
+ .concat(replacementSelector.elements.slice(1));
+
+ if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) {
+ path[path.length - 1].elements =
+ path[path.length - 1].elements.concat(newElements);
+ } else {
+ path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
+
+ path.push(new tree.Selector(
+ newElements
+ ));
+ }
+ currentSelectorPathIndex = match.endPathIndex;
+ currentSelectorPathElementIndex = match.endPathElementIndex;
+ if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) {
+ currentSelectorPathElementIndex = 0;
+ currentSelectorPathIndex++;
+ }
+ }
+
+ if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
+ path[path.length - 1].elements = path[path.length - 1]
+ .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
+ currentSelectorPathIndex++;
+ }
+
+ path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
+
+ return path;
+ },
+ visitRulesetOut: function (rulesetNode) {
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
+ newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
+ this.allExtendsStack.push(newAllExtends);
+ },
+ visitMediaOut: function (mediaNode) {
+ var lastIndex = this.allExtendsStack.length - 1;
+ this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
+ this.allExtendsStack.length = lastIndex;
+ },
+ visitDirective: function (directiveNode, visitArgs) {
+ var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
+ newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
+ this.allExtendsStack.push(newAllExtends);
+ },
+ visitDirectiveOut: function (directiveNode) {
+ var lastIndex = this.allExtendsStack.length - 1;
+ this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
+ this.allExtendsStack.length = lastIndex;
+ }
+};
+
+module.exports = ProcessExtendsVisitor;
+
+},{"../logger":31,"../tree":59,"./visitor":87}],82:[function(require,module,exports){
+function ImportSequencer(onSequencerEmpty) {
+ this.imports = [];
+ this.variableImports = [];
+ this._onSequencerEmpty = onSequencerEmpty;
+ this._currentDepth = 0;
+}
+
+ImportSequencer.prototype.addImport = function(callback) {
+ var importSequencer = this,
+ importItem = {
+ callback: callback,
+ args: null,
+ isReady: false
+ };
+ this.imports.push(importItem);
+ return function() {
+ importItem.args = Array.prototype.slice.call(arguments, 0);
+ importItem.isReady = true;
+ importSequencer.tryRun();
+ };
+};
+
+ImportSequencer.prototype.addVariableImport = function(callback) {
+ this.variableImports.push(callback);
+};
+
+ImportSequencer.prototype.tryRun = function() {
+ this._currentDepth++;
+ try {
+ while (true) {
+ while (this.imports.length > 0) {
+ var importItem = this.imports[0];
+ if (!importItem.isReady) {
+ return;
+ }
+ this.imports = this.imports.slice(1);
+ importItem.callback.apply(null, importItem.args);
+ }
+ if (this.variableImports.length === 0) {
+ break;
+ }
+ var variableImport = this.variableImports[0];
+ this.variableImports = this.variableImports.slice(1);
+ variableImport();
+ }
+ } finally {
+ this._currentDepth--;
+ }
+ if (this._currentDepth === 0 && this._onSequencerEmpty) {
+ this._onSequencerEmpty();
+ }
+};
+
+module.exports = ImportSequencer;
+
+},{}],83:[function(require,module,exports){
+var contexts = require("../contexts"),
+ Visitor = require("./visitor"),
+ ImportSequencer = require("./import-sequencer");
+
+var ImportVisitor = function(importer, finish) {
+
+ this._visitor = new Visitor(this);
+ this._importer = importer;
+ this._finish = finish;
+ this.context = new contexts.Eval();
+ this.importCount = 0;
+ this.onceFileDetectionMap = {};
+ this.recursionDetector = {};
+ this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this));
+};
+
+ImportVisitor.prototype = {
+ isReplacing: false,
+ run: function (root) {
+ try {
+ // process the contents
+ this._visitor.visit(root);
+ }
+ catch(e) {
+ this.error = e;
+ }
+
+ this.isFinished = true;
+ this._sequencer.tryRun();
+ },
+ _onSequencerEmpty: function() {
+ if (!this.isFinished) {
+ return;
+ }
+ this._finish(this.error);
+ },
+ visitImport: function (importNode, visitArgs) {
+ var inlineCSS = importNode.options.inline;
+
+ if (!importNode.css || inlineCSS) {
+
+ var context = new contexts.Eval(this.context, this.context.frames.slice(0));
+ var importParent = context.frames[0];
+
+ this.importCount++;
+ if (importNode.isVariableImport()) {
+ this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent));
+ } else {
+ this.processImportNode(importNode, context, importParent);
+ }
+ }
+ visitArgs.visitDeeper = false;
+ },
+ processImportNode: function(importNode, context, importParent) {
+ var evaldImportNode,
+ inlineCSS = importNode.options.inline;
+
+ try {
+ evaldImportNode = importNode.evalForImport(context);
+ } catch(e) {
+ if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
+ // attempt to eval properly and treat as css
+ importNode.css = true;
+ // if that fails, this error will be thrown
+ importNode.error = e;
+ }
+
+ if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
+
+ if (evaldImportNode.options.multiple) {
+ context.importMultiple = true;
+ }
+
+ // try appending if we haven't determined if it is css or not
+ var tryAppendLessExtension = evaldImportNode.css === undefined;
+
+ for (var i = 0; i < importParent.rules.length; i++) {
+ if (importParent.rules[i] === importNode) {
+ importParent.rules[i] = evaldImportNode;
+ break;
+ }
+ }
+
+ var onImported = this.onImported.bind(this, evaldImportNode, context),
+ sequencedOnImported = this._sequencer.addImport(onImported);
+
+ this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo,
+ evaldImportNode.options, sequencedOnImported);
+ } else {
+ this.importCount--;
+ if (this.isFinished) {
+ this._sequencer.tryRun();
+ }
+ }
+ },
+ onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
+ if (e) {
+ if (!e.filename) {
+ e.index = importNode.index; e.filename = importNode.currentFileInfo.filename;
+ }
+ this.error = e;
+ }
+
+ var importVisitor = this,
+ inlineCSS = importNode.options.inline,
+ duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
+
+ if (!context.importMultiple) {
+ if (duplicateImport) {
+ importNode.skip = true;
+ } else {
+ importNode.skip = function() {
+ if (fullPath in importVisitor.onceFileDetectionMap) {
+ return true;
+ }
+ importVisitor.onceFileDetectionMap[fullPath] = true;
+ return false;
+ };
+ }
+ }
+
+ if (root) {
+ importNode.root = root;
+ importNode.importedFilename = fullPath;
+
+ if (!inlineCSS && (context.importMultiple || !duplicateImport)) {
+ importVisitor.recursionDetector[fullPath] = true;
+
+ var oldContext = this.context;
+ this.context = context;
+ try {
+ this._visitor.visit(root);
+ } catch (e) {
+ this.error = e;
+ }
+ this.context = oldContext;
+ }
+ }
+
+ importVisitor.importCount--;
+
+ if (importVisitor.isFinished) {
+ importVisitor._sequencer.tryRun();
+ }
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitDirective: function (directiveNode, visitArgs) {
+ this.context.frames.unshift(directiveNode);
+ },
+ visitDirectiveOut: function (directiveNode) {
+ this.context.frames.shift();
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ this.context.frames.unshift(mixinDefinitionNode);
+ },
+ visitMixinDefinitionOut: function (mixinDefinitionNode) {
+ this.context.frames.shift();
+ },
+ visitRuleset: function (rulesetNode, visitArgs) {
+ this.context.frames.unshift(rulesetNode);
+ },
+ visitRulesetOut: function (rulesetNode) {
+ this.context.frames.shift();
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ this.context.frames.unshift(mediaNode.rules[0]);
+ },
+ visitMediaOut: function (mediaNode) {
+ this.context.frames.shift();
+ }
+};
+module.exports = ImportVisitor;
+
+},{"../contexts":10,"./import-sequencer":82,"./visitor":87}],84:[function(require,module,exports){
+var visitors = {
+ Visitor: require("./visitor"),
+ ImportVisitor: require('./import-visitor'),
+ ExtendVisitor: require('./extend-visitor'),
+ JoinSelectorVisitor: require('./join-selector-visitor'),
+ ToCSSVisitor: require('./to-css-visitor')
+};
+
+module.exports = visitors;
+
+},{"./extend-visitor":81,"./import-visitor":83,"./join-selector-visitor":85,"./to-css-visitor":86,"./visitor":87}],85:[function(require,module,exports){
+var Visitor = require("./visitor");
+
+var JoinSelectorVisitor = function() {
+ this.contexts = [[]];
+ this._visitor = new Visitor(this);
+};
+
+JoinSelectorVisitor.prototype = {
+ run: function (root) {
+ return this._visitor.visit(root);
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+
+ visitRuleset: function (rulesetNode, visitArgs) {
+ var context = this.contexts[this.contexts.length - 1],
+ paths = [], selectors;
+
+ this.contexts.push(paths);
+
+ if (! rulesetNode.root) {
+ selectors = rulesetNode.selectors;
+ if (selectors) {
+ selectors = selectors.filter(function(selector) { return selector.getIsOutput(); });
+ rulesetNode.selectors = selectors.length ? selectors : (selectors = null);
+ if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); }
+ }
+ if (!selectors) { rulesetNode.rules = null; }
+ rulesetNode.paths = paths;
+ }
+ },
+ visitRulesetOut: function (rulesetNode) {
+ this.contexts.length = this.contexts.length - 1;
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ var context = this.contexts[this.contexts.length - 1];
+ mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
+ }
+};
+
+module.exports = JoinSelectorVisitor;
+
+},{"./visitor":87}],86:[function(require,module,exports){
+var tree = require("../tree"),
+ Visitor = require("./visitor");
+
+var ToCSSVisitor = function(context) {
+ this._visitor = new Visitor(this);
+ this._context = context;
+};
+
+ToCSSVisitor.prototype = {
+ isReplacing: true,
+ run: function (root) {
+ return this._visitor.visit(root);
+ },
+
+ visitRule: function (ruleNode, visitArgs) {
+ if (ruleNode.variable) {
+ return;
+ }
+ return ruleNode;
+ },
+
+ visitMixinDefinition: function (mixinNode, visitArgs) {
+ // mixin definitions do not get eval'd - this means they keep state
+ // so we have to clear that state here so it isn't used if toCSS is called twice
+ mixinNode.frames = [];
+ },
+
+ visitExtend: function (extendNode, visitArgs) {
+ },
+
+ visitComment: function (commentNode, visitArgs) {
+ if (commentNode.isSilent(this._context)) {
+ return;
+ }
+ return commentNode;
+ },
+
+ visitMedia: function(mediaNode, visitArgs) {
+ mediaNode.accept(this._visitor);
+ visitArgs.visitDeeper = false;
+
+ if (!mediaNode.rules.length) {
+ return;
+ }
+ return mediaNode;
+ },
+
+ visitDirective: function(directiveNode, visitArgs) {
+ if (directiveNode.name === "@charset") {
+ if (!directiveNode.getIsReferenced()) {
+ return;
+ }
+ // Only output the debug info together with subsequent @charset definitions
+ // a comment (or @media statement) before the actual @charset directive would
+ // be considered illegal css as it has to be on the first line
+ if (this.charset) {
+ if (directiveNode.debugInfo) {
+ var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "") + " */\n");
+ comment.debugInfo = directiveNode.debugInfo;
+ return this._visitor.visit(comment);
+ }
+ return;
+ }
+ this.charset = true;
+ }
+ if (directiveNode.rules && directiveNode.rules.rules) {
+ this._mergeRules(directiveNode.rules.rules);
+ //process childs
+ directiveNode.accept(this._visitor);
+ visitArgs.visitDeeper = false;
+
+ // the directive was directly referenced and therefore needs to be shown in the output
+ if (directiveNode.getIsReferenced()) {
+ return directiveNode;
+ }
+
+ if (!directiveNode.rules.rules) {
+ return ;
+ }
+
+ //the directive was not directly referenced
+ for (var r = 0; r < directiveNode.rules.rules.length; r++) {
+ var rule = directiveNode.rules.rules[r];
+ if (rule.getIsReferenced && rule.getIsReferenced()) {
+ //the directive contains something that was referenced (likely by extend)
+ //therefore it needs to be shown in output too
+
+ //marking as referenced in case the directive is stored inside another directive
+ directiveNode.markReferenced();
+ return directiveNode;
+ }
+ }
+ //The directive was not directly referenced and does not contain anything that
+ //was referenced. Therefore it must not be shown in output.
+ return ;
+ } else {
+ if (!directiveNode.getIsReferenced()) {
+ return;
+ }
+ }
+ return directiveNode;
+ },
+
+ checkPropertiesInRoot: function(rules) {
+ var ruleNode;
+ for (var i = 0; i < rules.length; i++) {
+ ruleNode = rules[i];
+ if (ruleNode instanceof tree.Rule && !ruleNode.variable) {
+ throw { message: "properties must be inside selector blocks, they cannot be in the root.",
+ index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null};
+ }
+ }
+ },
+
+ visitRuleset: function (rulesetNode, visitArgs) {
+ var rule, rulesets = [];
+ if (rulesetNode.firstRoot) {
+ this.checkPropertiesInRoot(rulesetNode.rules);
+ }
+ if (! rulesetNode.root) {
+ if (rulesetNode.paths) {
+ rulesetNode.paths = rulesetNode.paths
+ .filter(function(p) {
+ var i;
+ if (p[0].elements[0].combinator.value === ' ') {
+ p[0].elements[0].combinator = new(tree.Combinator)('');
+ }
+ for (i = 0; i < p.length; i++) {
+ if (p[i].getIsReferenced() && p[i].getIsOutput()) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ // Compile rules and rulesets
+ var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0;
+ for (var i = 0; i < nodeRuleCnt; ) {
+ rule = nodeRules[i];
+ if (rule && rule.rules) {
+ // visit because we are moving them out from being a child
+ rulesets.push(this._visitor.visit(rule));
+ nodeRules.splice(i, 1);
+ nodeRuleCnt--;
+ continue;
+ }
+ i++;
+ }
+ // accept the visitor to remove rules and refactor itself
+ // then we can decide now whether we want it or not
+ if (nodeRuleCnt > 0) {
+ rulesetNode.accept(this._visitor);
+ } else {
+ rulesetNode.rules = null;
+ }
+ visitArgs.visitDeeper = false;
+
+ nodeRules = rulesetNode.rules;
+ if (nodeRules) {
+ this._mergeRules(nodeRules);
+ nodeRules = rulesetNode.rules;
+ }
+ if (nodeRules) {
+ this._removeDuplicateRules(nodeRules);
+ nodeRules = rulesetNode.rules;
+ }
+
+ // now decide whether we keep the ruleset
+ if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) {
+ rulesets.splice(0, 0, rulesetNode);
+ }
+ } else {
+ rulesetNode.accept(this._visitor);
+ visitArgs.visitDeeper = false;
+ if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) {
+ rulesets.splice(0, 0, rulesetNode);
+ }
+ }
+ if (rulesets.length === 1) {
+ return rulesets[0];
+ }
+ return rulesets;
+ },
+
+ _removeDuplicateRules: function(rules) {
+ if (!rules) { return; }
+
+ // remove duplicates
+ var ruleCache = {},
+ ruleList, rule, i;
+
+ for (i = rules.length - 1; i >= 0 ; i--) {
+ rule = rules[i];
+ if (rule instanceof tree.Rule) {
+ if (!ruleCache[rule.name]) {
+ ruleCache[rule.name] = rule;
+ } else {
+ ruleList = ruleCache[rule.name];
+ if (ruleList instanceof tree.Rule) {
+ ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)];
+ }
+ var ruleCSS = rule.toCSS(this._context);
+ if (ruleList.indexOf(ruleCSS) !== -1) {
+ rules.splice(i, 1);
+ } else {
+ ruleList.push(ruleCSS);
+ }
+ }
+ }
+ }
+ },
+
+ _mergeRules: function (rules) {
+ if (!rules) { return; }
+
+ var groups = {},
+ parts,
+ rule,
+ key;
+
+ for (var i = 0; i < rules.length; i++) {
+ rule = rules[i];
+
+ if ((rule instanceof tree.Rule) && rule.merge) {
+ key = [rule.name,
+ rule.important ? "!" : ""].join(",");
+
+ if (!groups[key]) {
+ groups[key] = [];
+ } else {
+ rules.splice(i--, 1);
+ }
+
+ groups[key].push(rule);
+ }
+ }
+
+ Object.keys(groups).map(function (k) {
+
+ function toExpression(values) {
+ return new (tree.Expression)(values.map(function (p) {
+ return p.value;
+ }));
+ }
+
+ function toValue(values) {
+ return new (tree.Value)(values.map(function (p) {
+ return p;
+ }));
+ }
+
+ parts = groups[k];
+
+ if (parts.length > 1) {
+ rule = parts[0];
+ var spacedGroups = [];
+ var lastSpacedGroup = [];
+ parts.map(function (p) {
+ if (p.merge === "+") {
+ if (lastSpacedGroup.length > 0) {
+ spacedGroups.push(toExpression(lastSpacedGroup));
+ }
+ lastSpacedGroup = [];
+ }
+ lastSpacedGroup.push(p);
+ });
+ spacedGroups.push(toExpression(lastSpacedGroup));
+ rule.value = toValue(spacedGroups);
+ }
+ });
+ }
+};
+
+module.exports = ToCSSVisitor;
+
+},{"../tree":59,"./visitor":87}],87:[function(require,module,exports){
+var tree = require("../tree");
+
+var _visitArgs = { visitDeeper: true },
+ _hasIndexed = false;
+
+function _noop(node) {
+ return node;
+}
+
+function indexNodeTypes(parent, ticker) {
+ // add .typeIndex to tree node types for lookup table
+ var key, child;
+ for (key in parent) {
+ if (parent.hasOwnProperty(key)) {
+ child = parent[key];
+ switch (typeof child) {
+ case "function":
+ // ignore bound functions directly on tree which do not have a prototype
+ // or aren't nodes
+ if (child.prototype && child.prototype.type) {
+ child.prototype.typeIndex = ticker++;
+ }
+ break;
+ case "object":
+ ticker = indexNodeTypes(child, ticker);
+ break;
+ }
+ }
+ }
+ return ticker;
+}
+
+var Visitor = function(implementation) {
+ this._implementation = implementation;
+ this._visitFnCache = [];
+
+ if (!_hasIndexed) {
+ indexNodeTypes(tree, 1);
+ _hasIndexed = true;
+ }
+};
+
+Visitor.prototype = {
+ visit: function(node) {
+ if (!node) {
+ return node;
+ }
+
+ var nodeTypeIndex = node.typeIndex;
+ if (!nodeTypeIndex) {
+ return node;
+ }
+
+ var visitFnCache = this._visitFnCache,
+ impl = this._implementation,
+ aryIndx = nodeTypeIndex << 1,
+ outAryIndex = aryIndx | 1,
+ func = visitFnCache[aryIndx],
+ funcOut = visitFnCache[outAryIndex],
+ visitArgs = _visitArgs,
+ fnName;
+
+ visitArgs.visitDeeper = true;
+
+ if (!func) {
+ fnName = "visit" + node.type;
+ func = impl[fnName] || _noop;
+ funcOut = impl[fnName + "Out"] || _noop;
+ visitFnCache[aryIndx] = func;
+ visitFnCache[outAryIndex] = funcOut;
+ }
+
+ if (func !== _noop) {
+ var newNode = func.call(impl, node, visitArgs);
+ if (impl.isReplacing) {
+ node = newNode;
+ }
+ }
+
+ if (visitArgs.visitDeeper && node && node.accept) {
+ node.accept(this);
+ }
+
+ if (funcOut != _noop) {
+ funcOut.call(impl, node);
+ }
+
+ return node;
+ },
+ visitArray: function(nodes, nonReplacing) {
+ if (!nodes) {
+ return nodes;
+ }
+
+ var cnt = nodes.length, i;
+
+ // Non-replacing
+ if (nonReplacing || !this._implementation.isReplacing) {
+ for (i = 0; i < cnt; i++) {
+ this.visit(nodes[i]);
+ }
+ return nodes;
+ }
+
+ // Replacing
+ var out = [];
+ for (i = 0; i < cnt; i++) {
+ var evald = this.visit(nodes[i]);
+ if (evald === undefined) { continue; }
+ if (!evald.splice) {
+ out.push(evald);
+ } else if (evald.length) {
+ this.flatten(evald, out);
+ }
+ }
+ return out;
+ },
+ flatten: function(arr, out) {
+ if (!out) {
+ out = [];
+ }
+
+ var cnt, i, item,
+ nestedCnt, j, nestedItem;
+
+ for (i = 0, cnt = arr.length; i < cnt; i++) {
+ item = arr[i];
+ if (item === undefined) {
+ continue;
+ }
+ if (!item.splice) {
+ out.push(item);
+ continue;
+ }
+
+ for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) {
+ nestedItem = item[j];
+ if (nestedItem === undefined) {
+ continue;
+ }
+ if (!nestedItem.splice) {
+ out.push(nestedItem);
+ } else if (nestedItem.length) {
+ this.flatten(nestedItem, out);
+ }
+ }
+ }
+
+ return out;
+ }
+};
+module.exports = Visitor;
+
+},{"../tree":59}],88:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ draining = true;
+ var currentQueue;
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ var i = -1;
+ while (++i < len) {
+ currentQueue[i]();
+ }
+ len = queue.length;
+ }
+ draining = false;
+}
+process.nextTick = function (fun) {
+ queue.push(fun);
+ if (!draining) {
+ setTimeout(drainQueue, 0);
+ }
+};
+
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],89:[function(require,module,exports){
+'use strict';
+
+var asap = require('asap')
+
+module.exports = Promise;
+function Promise(fn) {
+ if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
+ if (typeof fn !== 'function') throw new TypeError('not a function')
+ var state = null
+ var value = null
+ var deferreds = []
+ var self = this
+
+ this.then = function(onFulfilled, onRejected) {
+ return new self.constructor(function(resolve, reject) {
+ handle(new Handler(onFulfilled, onRejected, resolve, reject))
+ })
+ }
+
+ function handle(deferred) {
+ if (state === null) {
+ deferreds.push(deferred)
+ return
+ }
+ asap(function() {
+ var cb = state ? deferred.onFulfilled : deferred.onRejected
+ if (cb === null) {
+ (state ? deferred.resolve : deferred.reject)(value)
+ return
+ }
+ var ret
+ try {
+ ret = cb(value)
+ }
+ catch (e) {
+ deferred.reject(e)
+ return
+ }
+ deferred.resolve(ret)
+ })
+ }
+
+ function resolve(newValue) {
+ try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
+ if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
+ var then = newValue.then
+ if (typeof then === 'function') {
+ doResolve(then.bind(newValue), resolve, reject)
+ return
+ }
+ }
+ state = true
+ value = newValue
+ finale()
+ } catch (e) { reject(e) }
+ }
+
+ function reject(newValue) {
+ state = false
+ value = newValue
+ finale()
+ }
+
+ function finale() {
+ for (var i = 0, len = deferreds.length; i < len; i++)
+ handle(deferreds[i])
+ deferreds = null
+ }
+
+ doResolve(fn, resolve, reject)
+}
+
+
+function Handler(onFulfilled, onRejected, resolve, reject){
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null
+ this.resolve = resolve
+ this.reject = reject
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, onFulfilled, onRejected) {
+ var done = false;
+ try {
+ fn(function (value) {
+ if (done) return
+ done = true
+ onFulfilled(value)
+ }, function (reason) {
+ if (done) return
+ done = true
+ onRejected(reason)
+ })
+ } catch (ex) {
+ if (done) return
+ done = true
+ onRejected(ex)
+ }
+}
+
+},{"asap":91}],90:[function(require,module,exports){
+'use strict';
+
+//This file contains the ES6 extensions to the core Promises/A+ API
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+
+/* Static Functions */
+
+function ValuePromise(value) {
+ this.then = function (onFulfilled) {
+ if (typeof onFulfilled !== 'function') return this
+ return new Promise(function (resolve, reject) {
+ asap(function () {
+ try {
+ resolve(onFulfilled(value))
+ } catch (ex) {
+ reject(ex);
+ }
+ })
+ })
+ }
+}
+ValuePromise.prototype = Promise.prototype
+
+var TRUE = new ValuePromise(true)
+var FALSE = new ValuePromise(false)
+var NULL = new ValuePromise(null)
+var UNDEFINED = new ValuePromise(undefined)
+var ZERO = new ValuePromise(0)
+var EMPTYSTRING = new ValuePromise('')
+
+Promise.resolve = function (value) {
+ if (value instanceof Promise) return value
+
+ if (value === null) return NULL
+ if (value === undefined) return UNDEFINED
+ if (value === true) return TRUE
+ if (value === false) return FALSE
+ if (value === 0) return ZERO
+ if (value === '') return EMPTYSTRING
+
+ if (typeof value === 'object' || typeof value === 'function') {
+ try {
+ var then = value.then
+ if (typeof then === 'function') {
+ return new Promise(then.bind(value))
+ }
+ } catch (ex) {
+ return new Promise(function (resolve, reject) {
+ reject(ex)
+ })
+ }
+ }
+
+ return new ValuePromise(value)
+}
+
+Promise.all = function (arr) {
+ var args = Array.prototype.slice.call(arr)
+
+ return new Promise(function (resolve, reject) {
+ if (args.length === 0) return resolve([])
+ var remaining = args.length
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then
+ if (typeof then === 'function') {
+ then.call(val, function (val) { res(i, val) }, reject)
+ return
+ }
+ }
+ args[i] = val
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex)
+ }
+ }
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i])
+ }
+ })
+}
+
+Promise.reject = function (value) {
+ return new Promise(function (resolve, reject) {
+ reject(value);
+ });
+}
+
+Promise.race = function (values) {
+ return new Promise(function (resolve, reject) {
+ values.forEach(function(value){
+ Promise.resolve(value).then(resolve, reject);
+ })
+ });
+}
+
+/* Prototype Methods */
+
+Promise.prototype['catch'] = function (onRejected) {
+ return this.then(null, onRejected);
+}
+
+},{"./core.js":89,"asap":91}],91:[function(require,module,exports){
+(function (process){
+
+// Use the fastest possible means to execute a task in a future turn
+// of the event loop.
+
+// linked list of tasks (single, with head node)
+var head = {task: void 0, next: null};
+var tail = head;
+var flushing = false;
+var requestFlush = void 0;
+var isNodeJS = false;
+
+function flush() {
+ /* jshint loopfunc: true */
+
+ while (head.next) {
+ head = head.next;
+ var task = head.task;
+ head.task = void 0;
+ var domain = head.domain;
+
+ if (domain) {
+ head.domain = void 0;
+ domain.enter();
+ }
+
+ try {
+ task();
+
+ } catch (e) {
+ if (isNodeJS) {
+ // In node, uncaught exceptions are considered fatal errors.
+ // Re-throw them synchronously to interrupt flushing!
+
+ // Ensure continuation if the uncaught exception is suppressed
+ // listening "uncaughtException" events (as domains does).
+ // Continue in next event to avoid tick recursion.
+ if (domain) {
+ domain.exit();
+ }
+ setTimeout(flush, 0);
+ if (domain) {
+ domain.enter();
+ }
+
+ throw e;
+
+ } else {
+ // In browsers, uncaught exceptions are not fatal.
+ // Re-throw them asynchronously to avoid slow-downs.
+ setTimeout(function() {
+ throw e;
+ }, 0);
+ }
+ }
+
+ if (domain) {
+ domain.exit();
+ }
+ }
+
+ flushing = false;
+}
+
+if (typeof process !== "undefined" && process.nextTick) {
+ // Node.js before 0.9. Note that some fake-Node environments, like the
+ // Mocha test runner, introduce a `process` global without a `nextTick`.
+ isNodeJS = true;
+
+ requestFlush = function () {
+ process.nextTick(flush);
+ };
+
+} else if (typeof setImmediate === "function") {
+ // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
+ if (typeof window !== "undefined") {
+ requestFlush = setImmediate.bind(window, flush);
+ } else {
+ requestFlush = function () {
+ setImmediate(flush);
+ };
+ }
+
+} else if (typeof MessageChannel !== "undefined") {
+ // modern browsers
+ // http://www.nonblocking.io/2011/06/windownexttick.html
+ var channel = new MessageChannel();
+ channel.port1.onmessage = flush;
+ requestFlush = function () {
+ channel.port2.postMessage(0);
+ };
+
+} else {
+ // old browsers
+ requestFlush = function () {
+ setTimeout(flush, 0);
+ };
+}
+
+function asap(task) {
+ tail = tail.next = {
+ task: task,
+ domain: isNodeJS && process.domain,
+ next: null
+ };
+
+ if (!flushing) {
+ flushing = true;
+ requestFlush();
+ }
+};
+
+module.exports = asap;
+
+
+}).call(this,require('_process'))
+},{"_process":88}],92:[function(require,module,exports){
+// should work in any browser without browserify
+
+if (typeof Promise.prototype.done !== 'function') {
+ Promise.prototype.done = function (onFulfilled, onRejected) {
+ var self = arguments.length ? this.then.apply(this, arguments) : this
+ self.then(null, function (err) {
+ setTimeout(function () {
+ throw err
+ }, 0)
+ })
+ }
+}
+},{}],"promise/polyfill.js":[function(require,module,exports){
+// not "use strict" so we can declare global "Promise"
+
+var asap = require('asap');
+
+if (typeof Promise === 'undefined') {
+ Promise = require('./lib/core.js')
+ require('./lib/es6-extensions.js')
+}
+
+require('./polyfill-done.js');
+
+},{"./lib/core.js":89,"./lib/es6-extensions.js":90,"./polyfill-done.js":92,"asap":91}]},{},[2])(2)
+});
\ No newline at end of file
--- /dev/null
+/*!
+ * Less - Leaner CSS v2.4.0
+ * http://lesscss.org
+ *
+ * Copyright (c) 2009-2015, Alexis Sellier <self@cloudhead.net>
+ * Licensed under the Apache v2 License.
+ *
+ */
+
+ /** * @license Apache v2
+ */
+
+!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.less=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;d.length>g;g++)e(d[g]);return e}({1:[function(a,b){var c=a("./utils").addDataAttr,d=a("./browser");b.exports=function(a,b){c(b,d.currentScript(a)),void 0===b.isFileProtocol&&(b.isFileProtocol=/^(file|(chrome|safari)(-extension)?|resource|qrc|app):/.test(a.location.protocol)),b.async=b.async||!1,b.fileAsync=b.fileAsync||!1,b.poll=b.poll||(b.isFileProtocol?1e3:1500),b.env=b.env||("127.0.0.1"==a.location.hostname||"0.0.0.0"==a.location.hostname||"localhost"==a.location.hostname||a.location.port&&a.location.port.length>0||b.isFileProtocol?"development":"production");var e=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(a.location.hash);e&&(b.dumpLineNumbers=e[1]),void 0===b.useFileCache&&(b.useFileCache=!0),void 0===b.onReady&&(b.onReady=!0)}},{"./browser":3,"./utils":9}],2:[function(a,b){a("promise/polyfill.js");var c=window.less||{};a("./add-default-options")(window,c);var d=b.exports=a("./index")(window,c);c.onReady&&(/!watch/.test(window.location.hash)&&d.watch(),d.pageLoadFinished=d.registerStylesheets().then(function(){return d.refresh("development"===d.env)}))},{"./add-default-options":1,"./index":7,"promise/polyfill.js":"promise/polyfill.js"}],3:[function(a,b){var c=a("./utils");b.exports={createCSS:function(a,b,d){var e=d.href||"",f="less:"+(d.title||c.extractId(e)),g=a.getElementById(f),h=!1,i=a.createElement("style");i.setAttribute("type","text/css"),d.media&&i.setAttribute("media",d.media),i.id=f,i.styleSheet||(i.appendChild(a.createTextNode(b)),h=null!==g&&g.childNodes.length>0&&i.childNodes.length>0&&g.firstChild.nodeValue===i.firstChild.nodeValue);var j=a.getElementsByTagName("head")[0];if(null===g||h===!1){var k=d&&d.nextSibling||null;k?k.parentNode.insertBefore(i,k):j.appendChild(i)}if(g&&h===!1&&g.parentNode.removeChild(g),i.styleSheet)try{i.styleSheet.cssText=b}catch(l){throw new Error("Couldn't reassign styleSheet.cssText.")}},currentScript:function(a){var b=a.document;return b.currentScript||function(){var a=b.getElementsByTagName("script");return a[a.length-1]}()}}},{"./utils":9}],4:[function(a,b){b.exports=function(a,b,c){var d=null;if("development"!==b.env)try{d="undefined"==typeof a.localStorage?null:a.localStorage}catch(e){}return{setCSS:function(a,b,e){if(d){c.info("saving "+a+" to cache.");try{d.setItem(a,e),d.setItem(a+":timestamp",b)}catch(f){c.error('failed to save "'+a+'" to local storage for caching.')}}},getCSS:function(a,b){var c=d&&d.getItem(a),e=d&&d.getItem(a+":timestamp");return e&&b.lastModified&&new Date(b.lastModified).valueOf()===new Date(e).valueOf()?c:void 0}}}},{}],5:[function(a,b){var c=a("./utils"),d=a("./browser");b.exports=function(a,b,e){function f(b,f){var g,h,i="less-error-message:"+c.extractId(f||""),j='<li><label>{line}</label><pre class="{class}">{content}</pre></li>',k=a.document.createElement("div"),l=[],m=b.filename||f,n=m.match(/([^\/]+(\?.*)?)$/)[1];k.id=i,k.className="less-error-message",h="<h3>"+(b.type||"Syntax")+"Error: "+(b.message||"There is an error in your .less file")+'</h3><p>in <a href="'+m+'">'+n+"</a> ";var o=function(a,b,c){void 0!==a.extract[b]&&l.push(j.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};b.extract&&(o(b,0,""),o(b,1,"line"),o(b,2,""),h+="on line "+b.line+", column "+(b.column+1)+":</p><ul>"+l.join("")+"</ul>"),b.stack&&(b.extract||e.logLevel>=4)&&(h+="<br/>Stack Trace</br />"+b.stack.split("\n").slice(1).join("<br/>")),k.innerHTML=h,d.createCSS(a.document,[".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),k.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"===e.env&&(g=setInterval(function(){var b=a.document,c=b.body;c&&(b.getElementById(i)?c.replaceChild(k,b.getElementById(i)):c.insertBefore(k,c.firstChild),clearInterval(g))},10))}function g(a,b){e.errorReporting&&"html"!==e.errorReporting?"console"===e.errorReporting?k(a,b):"function"==typeof e.errorReporting&&e.errorReporting("add",a,b):f(a,b)}function h(b){var d=a.document.getElementById("less-error-message:"+c.extractId(b));d&&d.parentNode.removeChild(d)}function i(){}function j(a){e.errorReporting&&"html"!==e.errorReporting?"console"===e.errorReporting?i(a):"function"==typeof e.errorReporting&&e.errorReporting("remove",a):h(a)}function k(a,c){var d="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,b,c){void 0!==a.extract[b]&&g.push(d.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.extract&&(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")),a.stack&&(a.extract||e.logLevel>=4)&&(h+="\nStack Trace\n"+a.stack),b.logger.error(h)}return{add:g,remove:j}}},{"./browser":3,"./utils":9}],6:[function(a,b){b.exports=function(b,c){function d(){if(window.XMLHttpRequest&&!("file:"===window.location.protocol&&"ActiveXObject"in window))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(a){return c.error("browser doesn't support AJAX."),null}}var e=a("../less/environment/abstract-file-manager.js"),f={},g=function(){};return g.prototype=new e,g.prototype.alwaysMakePathsAbsolute=function(){return!0},g.prototype.join=function(a,b){return a?this.extractUrlParts(b,a).path:b},g.prototype.doXHR=function(a,e,f,g){function h(b,c,d){b.status>=200&&300>b.status?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var i=d(),j=b.isFileProtocol?b.fileAsync:b.async;"function"==typeof i.overrideMimeType&&i.overrideMimeType("text/css"),c.debug("XHR: Getting '"+a+"'"),i.open("GET",a,j),i.setRequestHeader("Accept",e||"text/x-less, text/css; q=0.9, */*; q=0.5"),i.send(null),b.isFileProtocol&&!b.fileAsync?0===i.status||i.status>=200&&300>i.status?f(i.responseText):g(i.status,a):j?i.onreadystatechange=function(){4==i.readyState&&h(i,f,g)}:h(i,f,g)},g.prototype.supports=function(){return!0},g.prototype.clearFileCache=function(){f={}},g.prototype.loadFile=function(a,b,c,d,e){b&&!this.isPathAbsolute(a)&&(a=b+a),c=c||{};var g=this.extractUrlParts(a,window.location.href),h=g.url;if(c.useFileCache&&f[h])try{var i=f[h];e(null,{contents:i,filename:h,webInfo:{lastModified:new Date}})}catch(j){e({filename:h,message:"Error loading file "+h+" error was "+j.message})}else this.doXHR(h,c.mime,function(a,b){f[h]=a,e(null,{contents:a,filename:h,webInfo:{lastModified:b}})},function(a,b){e({type:"File",message:"'"+b+"' wasn't found ("+a+")",href:h})})},g}},{"../less/environment/abstract-file-manager.js":14}],7:[function(a,b){var c=a("./utils").addDataAttr,d=a("./browser");b.exports=function(b,e){function f(a){return e.postProcessor&&"function"==typeof e.postProcessor&&(a=e.postProcessor.call(a,a)||a),a}function g(a){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b}function h(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){var d=c.concat(Array.prototype.slice.call(arguments,0));return a.apply(b,d)}}function i(a){for(var b,c=m.getElementsByTagName("style"),d=0;c.length>d;d++)if(b=c[d],b.type.match(t)){var f=g(e);f.modifyVars=a;var i=b.innerHTML||"";f.filename=m.location.href.replace(/#.*$/,""),n.render(i,f,h(function(a,b,c){b?r.add(b,"inline"):(a.type="text/css",a.styleSheet?a.styleSheet.cssText=c.css:a.innerHTML=c.css)},null,b))}}function j(a,b,d,h,i){function j(c){var e=c.contents,g=c.filename,i=c.webInfo,j={currentDirectory:q.getPath(g),filename:g,rootFilename:g,relativeUrls:k.relativeUrls};if(j.entryPath=j.currentDirectory,j.rootpath=k.rootpath||j.currentDirectory,i&&(i.remaining=h,!k.modifyVars)){var l=s.getCSS(g,i);if(!d&&l)return i.local=!0,void b(null,l,e,a,i,g)}r.remove(g),k.rootFileInfo=j,n.render(e,k,function(c,d){c?(c.href=g,b(c)):(d.css=f(d.css),k.modifyVars||s.setCSS(a.href,i.lastModified,d.css),b(null,d.css,e,a,i,g))})}var k=g(e);c(k,a),k.mime=a.type,i&&(k.modifyVars=i),q.loadFile(a.href,null,k,o,function(a,c){return a?void b(a):void j(c)})}function k(a,b,c){for(var d=0;n.sheets.length>d;d++)j(n.sheets[d],a,b,n.sheets.length-(d+1),c)}function l(){"development"===n.env&&(n.watchTimer=setInterval(function(){n.watchMode&&(q.clearFileCache(),k(function(a,c,e,f){a?r.add(a,a.href||f.href):c&&d.createCSS(b.document,c,f)}))},e.poll))}var m=b.document,n=a("../less")();n.options=e;var o=n.environment,p=a("./file-manager")(e,n.logger),q=new p;o.addFileManager(q),n.FileManager=p,a("./log-listener")(n,e);var r=a("./error-reporting")(b,n,e),s=n.cache=e.cache||a("./cache")(b,e,n.logger);e.functions&&n.functions.functionRegistry.addMultiple(e.functions);var t=/^text\/(x-)?less$/;return n.watch=function(){return n.watchMode||(n.env="development",l()),this.watchMode=!0,!0},n.unwatch=function(){return clearInterval(n.watchTimer),this.watchMode=!1,!1},n.registerStylesheets=function(){return new Promise(function(a){var b=m.getElementsByTagName("link");n.sheets=[];for(var c=0;b.length>c;c++)("stylesheet/less"===b[c].rel||b[c].rel.match(/stylesheet/)&&b[c].type.match(t))&&n.sheets.push(b[c]);a()})},n.modifyVars=function(a){return n.refresh(!0,a,!1)},n.refresh=function(a,c,e){return(a||e)&&e!==!1&&q.clearFileCache(),new Promise(function(e,f){var g,h,j;g=h=new Date,k(function(a,c,i,k,l){return a?(r.add(a,a.href||k.href),void f(a)):(n.logger.info(l.local?"loading "+k.href+" from cache.":"rendered "+k.href+" successfully."),d.createCSS(b.document,c,k),n.logger.info("css for "+k.href+" generated in "+(new Date-h)+"ms"),0===l.remaining&&(j=new Date-g,n.logger.info("less has finished. css generated in "+j+"ms"),e({startTime:g,endTime:h,totalMilliseconds:j,sheets:n.sheets.length})),void(h=new Date))},a,c),i(c)})},n.refreshStyles=i,n}},{"../less":29,"./browser":3,"./cache":4,"./error-reporting":5,"./file-manager":6,"./log-listener":8,"./utils":9}],8:[function(a,b){b.exports=function(a,b){var c=4,d=3,e=2,f=1;b.logLevel="undefined"!=typeof b.logLevel?b.logLevel:"development"===b.env?d:f,b.loggers||(b.loggers=[{debug:function(a){b.logLevel>=c&&console.log(a)},info:function(a){b.logLevel>=d&&console.log(a)},warn:function(a){b.logLevel>=e&&console.warn(a)},error:function(a){b.logLevel>=f&&console.error(a)}}]);for(var g=0;b.loggers.length>g;g++)a.logger.addListener(b.loggers[g])}},{}],9:[function(a,b){b.exports={extractId:function(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/[\?\&]livereload=\w+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")},addDataAttr:function(a,b){for(var c in b.dataset)if(b.dataset.hasOwnProperty(c))if("env"===c||"dumpLineNumbers"===c||"rootpath"===c||"errorReporting"===c)a[c]=b.dataset[c];else try{a[c]=JSON.parse(b.dataset[c])}catch(d){}}}},{}],10:[function(a,b){var c={};b.exports=c;var d=function(a,b,c){if(a)for(var d=0;c.length>d;d++)a.hasOwnProperty(c[d])&&(b[c[d]]=a[c[d]])},e=["paths","relativeUrls","rootpath","strictImports","insecure","dumpLineNumbers","compress","syncImport","chunkInput","mime","useFileCache","processImports","reference","pluginManager"];c.Parse=function(a){d(a,this,e),"string"==typeof this.paths&&(this.paths=[this.paths])};var f=["paths","compress","ieCompat","strictMath","strictUnits","sourceMap","importMultiple","urlArgs","javascriptEnabled","pluginManager","importantScope"];c.Eval=function(a,b){d(a,this,f),this.frames=b||[],this.importantScope=this.importantScope||[]},c.Eval.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},c.Eval.prototype.outOfParenthesis=function(){this.parensStack.pop()},c.Eval.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},c.Eval.prototype.isPathRelative=function(a){return!/^(?:[a-z-]+:|\/|#)/i.test(a)},c.Eval.prototype.normalizePath=function(a){var b,c=a.split("/").reverse();for(a=[];0!==c.length;)switch(b=c.pop()){case".":break;case"..":0===a.length||".."===a[a.length-1]?a.push(b):a.pop();break;default:a.push(b)}return a.join("/")}},{}],11:[function(a,b){b.exports={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"}},{}],12:[function(a,b){b.exports={colors:a("./colors"),unitConversions:a("./unit-conversions")}},{"./colors":11,"./unit-conversions":13}],13:[function(a,b){b.exports={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}}},{}],14:[function(a,b){var c=function(){};c.prototype.getPath=function(a){var b=a.lastIndexOf("?");return b>0&&(a=a.slice(0,b)),b=a.lastIndexOf("/"),0>b&&(b=a.lastIndexOf("\\")),0>b?"":a.slice(0,b+1)},c.prototype.tryAppendLessExtension=function(a){return/(\.[a-z]*$)|([\?;].*)$/.test(a)?a:a+".less"},c.prototype.supportsSync=function(){return!1},c.prototype.alwaysMakePathsAbsolute=function(){return!1},c.prototype.isPathAbsolute=function(a){return/^(?:[a-z-]+:|\/|\\|#)/i.test(a)},c.prototype.join=function(a,b){return a?a+b:b},c.prototype.pathDiff=function(a,b){var c,d,e,f,g=this.extractUrlParts(a),h=this.extractUrlParts(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;f.length-1>c;c++)i+="../";for(c=0;e.length-1>c;c++)i+=e[c]+"/";return i},c.prototype.extractUrlParts=function(a,b){var c,d,e=/^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,f=a.match(e),g={},h=[];if(!f)throw new Error("Could not parse sheet href - '"+a+"'");if(b&&(!f[1]||f[2])){if(d=b.match(e),!d)throw new Error("Could not parse page url - '"+b+"'");f[1]=f[1]||d[1]||"",f[2]||(f[3]=d[3]+f[3])}if(f[3]){for(h=f[3].replace(/\\/g,"/").split("/"),c=0;h.length>c;c++)"."===h[c]&&(h.splice(c,1),c-=1);for(c=0;h.length>c;c++)".."===h[c]&&c>0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=(f[1]||"")+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g},b.exports=c},{}],15:[function(a,b){var c=a("../logger"),d=function(a,b){this.fileManagers=b||[],a=a||{};for(var c=["encodeBase64","mimeLookup","charsetLookup","getSourceMapGenerator"],d=[],e=d.concat(c),f=0;e.length>f;f++){var g=e[f],h=a[g];h?this[g]=h.bind(a):d.length>f&&this.warn("missing required function in environment - "+g)}};d.prototype.getFileManager=function(a,b,d,e,f){a||c.warn("getFileManager called with no filename.. Please report this issue. continuing."),null==b&&c.warn("getFileManager called with null directory.. Please report this issue. continuing.");var g=this.fileManagers;d.pluginManager&&(g=[].concat(g).concat(d.pluginManager.getFileManagers()));for(var h=g.length-1;h>=0;h--){var i=g[h];if(i[f?"supportsSync":"supports"](a,b,d,e))return i}return null},d.prototype.addFileManager=function(a){this.fileManagers.push(a)},d.prototype.clearFileManagers=function(){this.fileManagers=[]},b.exports=d},{"../logger":31}],16:[function(a){function b(a,b,d){var e,f,g,h,i=b.alpha,j=d.alpha,k=[];g=j+i*(1-j);for(var l=0;3>l;l++)e=b.rgb[l]/255,f=d.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new c(k,g)}var c=a("../tree/color"),d=a("./function-registry"),e={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?e.multiply(a,b):e.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return e.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};for(var f in e)e.hasOwnProperty(f)&&(b[f]=b.bind(null,e[f]));d.addMultiple(b)},{"../tree/color":47,"./function-registry":21}],17:[function(a){function b(a){return Math.min(1,Math.max(0,a))}function c(a){return f.hsla(a.h,a.s,a.l,a.a)}function d(a){if(a instanceof g)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{type:"Argument",message:"color functions take numbers as parameters"}}function e(a,b){return a instanceof g&&a.unit.is("%")?parseFloat(a.value*b/100):d(a)}var f,g=a("../tree/dimension"),h=a("../tree/color"),i=a("../tree/quoted"),j=a("../tree/anonymous"),k=a("./function-registry");f={rgb:function(a,b,c){return f.rgba(a,b,c,1)},rgba:function(a,b,c,f){var g=[a,b,c].map(function(a){return e(a,255)});return f=d(f),new h(g,f)},hsl:function(a,b,c){return f.hsla(a,b,c,1)},hsla:function(a,c,e,g){function h(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?j+(i-j)*a*6:1>2*a?i:2>3*a?j+(i-j)*(2/3-a)*6:j}a=d(a)%360/360,c=b(d(c)),e=b(d(e)),g=b(d(g));var i=.5>=e?e*(c+1):e+c-e*c,j=2*e-i;return f.rgba(255*h(a+1/3),255*h(a),255*h(a-1/3),g)},hsv:function(a,b,c){return f.hsva(a,b,c,1)},hsva:function(a,b,c,e){a=d(a)%360/360*360,b=d(b),c=d(c),e=d(e);var g,h;g=Math.floor(a/60%6),h=a/60-g;var i=[c,c*(1-b),c*(1-h*b),c*(1-(1-h)*b)],j=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return f.rgba(255*i[j[g][0]],255*i[j[g][1]],255*i[j[g][2]],e)},hue:function(a){return new g(a.toHSL().h)},saturation:function(a){return new g(100*a.toHSL().s,"%")},lightness:function(a){return new g(100*a.toHSL().l,"%")},hsvhue:function(a){return new g(a.toHSV().h)},hsvsaturation:function(a){return new g(100*a.toHSV().s,"%")},hsvvalue:function(a){return new g(100*a.toHSV().v,"%")},red:function(a){return new g(a.rgb[0])},green:function(a){return new g(a.rgb[1])},blue:function(a){return new g(a.rgb[2])},alpha:function(a){return new g(a.toHSL().a)},luma:function(a){return new g(a.luma()*a.alpha*100,"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new g(b*a.alpha*100,"%")},saturate:function(a,d){if(!a.rgb)return null;var e=a.toHSL();return e.s+=d.value/100,e.s=b(e.s),c(e)},desaturate:function(a,d){var e=a.toHSL();return e.s-=d.value/100,e.s=b(e.s),c(e)},lighten:function(a,d){var e=a.toHSL();return e.l+=d.value/100,e.l=b(e.l),c(e)},darken:function(a,d){var e=a.toHSL();return e.l-=d.value/100,e.l=b(e.l),c(e)},fadein:function(a,d){var e=a.toHSL();return e.a+=d.value/100,e.a=b(e.a),c(e)},fadeout:function(a,d){var e=a.toHSL();return e.a-=d.value/100,e.a=b(e.a),c(e)},fade:function(a,d){var e=a.toHSL();return e.a=d.value/100,e.a=b(e.a),c(e)},spin:function(a,b){var d=a.toHSL(),e=(d.h+b.value)%360;return d.h=0>e?360+e:e,c(d)},mix:function(a,b,c){c||(c=new g(50));var d=c.value/100,e=2*d-1,f=a.toHSL().a-b.toHSL().a,i=((e*f==-1?e:(e+f)/(1+e*f))+1)/2,j=1-i,k=[a.rgb[0]*i+b.rgb[0]*j,a.rgb[1]*i+b.rgb[1]*j,a.rgb[2]*i+b.rgb[2]*j],l=a.alpha*d+b.alpha*(1-d);return new h(k,l)},greyscale:function(a){return f.desaturate(a,new g(100))},contrast:function(a,b,c,e){if(!a.rgb)return null;if("undefined"==typeof c&&(c=f.rgba(255,255,255,1)),"undefined"==typeof b&&(b=f.rgba(0,0,0,1)),b.luma()>c.luma()){var g=c;c=b,b=g}return e="undefined"==typeof e?.43:d(e),a.luma()<e?c:b},argb:function(a){return new j(a.toARGB())},color:function(a){if(a instanceof i&&/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(a.value))return new h(a.value.slice(1));if(a instanceof h||(a=h.fromKeyword(a.value)))return a.value=void 0,a;throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}},tint:function(a,b){return f.mix(f.rgb(255,255,255),a,b)},shade:function(a,b){return f.mix(f.rgb(0,0,0),a,b)}},k.addMultiple(f)},{"../tree/anonymous":43,"../tree/color":47,"../tree/dimension":53,"../tree/quoted":70,"./function-registry":21}],18:[function(a,b){b.exports=function(b){var c=a("../tree/quoted"),d=a("../tree/url"),e=a("./function-registry"),f=function(a,b){return new d(b,a.index,a.currentFileInfo).eval(a.context)},g=a("../logger");e.add("data-uri",function(a,e){e||(e=a,a=null);var h=a&&a.value,i=e.value,j=this.currentFileInfo,k=j.relativeUrls?j.currentDirectory:j.entryPath,l=i.indexOf("#"),m="";-1!==l&&(m=i.slice(l),i=i.slice(0,l));var n=b.getFileManager(i,k,this.context,b,!0);if(!n)return f(this,e);var o=!1;if(a)o=/;base64$/.test(h);else{if(h=b.mimeLookup(i),"image/svg+xml"===h)o=!1;else{var p=b.charsetLookup(h);o=["US-ASCII","UTF-8"].indexOf(p)<0}o&&(h+=";base64")}var q=n.loadFileSync(i,k,this.context,b);if(!q.contents)return g.warn("Skipped data-uri embedding of "+i+" because file not found"),f(this,e||a);var r=q.contents;if(o&&!b.encodeBase64)return f(this,e);r=o?b.encodeBase64(r):encodeURIComponent(r);var s="data:"+h+","+r+m,t=32768;return s.length>=t&&this.context.ieCompat!==!1?(g.warn("Skipped data-uri embedding of "+i+" because its size ("+s.length+" characters) exceeds IE8-safe "+t+" characters!"),f(this,e||a)):new d(new c('"'+s+'"',s,!1,this.index,this.currentFileInfo),this.index,this.currentFileInfo)})}},{"../logger":31,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],19:[function(a,b){var c=a("../tree/keyword"),d=a("./function-registry"),e={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?c.True:c.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}};d.add("default",e.eval.bind(e)),b.exports=e},{"../tree/keyword":62,"./function-registry":21}],20:[function(a,b){var c=a("./function-registry"),d=function(a,b,d,e){this.name=a.toLowerCase(),this.func=c.get(this.name),this.index=d,this.context=b,this.currentFileInfo=e};d.prototype.isValid=function(){return Boolean(this.func)},d.prototype.call=function(a){return this.func.apply(this,a)},b.exports=d},{"./function-registry":21}],21:[function(a,b){b.exports={_data:{},add:function(a,b){this._data.hasOwnProperty(a),this._data[a]=b},addMultiple:function(a){Object.keys(a).forEach(function(b){this.add(b,a[b])}.bind(this))},get:function(a){return this._data[a]}}},{}],22:[function(a,b){b.exports=function(b){var c={functionRegistry:a("./function-registry"),functionCaller:a("./function-caller")};return a("./default"),a("./color"),a("./color-blending"),a("./data-uri")(b),a("./math"),a("./number"),a("./string"),a("./svg")(b),a("./types"),c}},{"./color":17,"./color-blending":16,"./data-uri":18,"./default":19,"./function-caller":20,"./function-registry":21,"./math":23,"./number":24,"./string":25,"./svg":26,"./types":27}],23:[function(a){function b(a,b,d){if(!(d instanceof c))throw{type:"Argument",message:"argument must be a number"};return null==b?b=d.unit:d=d.unify(),new c(a(parseFloat(d.value)),b)}var c=a("../tree/dimension"),d=a("./function-registry"),e={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"};for(var f in e)e.hasOwnProperty(f)&&(e[f]=b.bind(null,Math[f],e[f]));e.round=function(a,c){var d="undefined"==typeof c?0:c.value;return b(function(a){return a.toFixed(d)},null,a)},d.addMultiple(e)},{"../tree/dimension":53,"./function-registry":21}],24:[function(a){var b=a("../tree/dimension"),c=a("../tree/anonymous"),d=a("./function-registry"),e=function(a,d){switch(d=Array.prototype.slice.call(d),d.length){case 0:throw{type:"Argument",message:"one or more arguments required"}}var e,f,g,h,i,j,k,l,m=[],n={};for(e=0;d.length>e;e++)if(g=d[e],g instanceof b)if(h=""===g.unit.toString()&&void 0!==l?new b(g.value,l).unify():g.unify(),j=""===h.unit.toString()&&void 0!==k?k:h.unit.toString(),k=""!==j&&void 0===k||""!==j&&""===m[0].unify().unit.toString()?j:k,l=""!==j&&void 0===l?g.unit.toString():l,f=void 0!==n[""]&&""!==j&&j===k?n[""]:n[j],void 0!==f)i=""===m[f].unit.toString()&&void 0!==l?new b(m[f].value,l).unify():m[f].unify(),(a&&i.value>h.value||!a&&h.value>i.value)&&(m[f]=g);else{if(void 0!==k&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(d[e].value)&&Array.prototype.push.apply(d,Array.prototype.slice.call(d[e].value));return 1==m.length?m[0]:(d=m.map(function(a){return a.toCSS(this.context)}).join(this.context.compress?",":", "),new c((a?"min":"max")+"("+d+")"))};d.addMultiple({min:function(){return e(!0,arguments)},max:function(){return e(!1,arguments)},convert:function(a,b){return a.convertTo(b.value)},pi:function(){return new b(Math.PI)},mod:function(a,c){return new b(a.value%c.value,a.unit)},pow:function(a,c){if("number"==typeof a&&"number"==typeof c)a=new b(a),c=new b(c);else if(!(a instanceof b&&c instanceof b))throw{type:"Argument",message:"arguments must be numbers"};return new b(Math.pow(a.value,c.value),a.unit)},percentage:function(a){return new b(100*a.value,"%")}})},{"../tree/anonymous":43,"../tree/dimension":53,"./function-registry":21}],25:[function(a){var b=a("../tree/quoted"),c=a("../tree/anonymous"),d=a("../tree/javascript"),e=a("./function-registry");e.addMultiple({e:function(a){return new c(a instanceof d?a.evaluated:a.value)},escape:function(a){return new c(encodeURI(a.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},replace:function(a,c,d,e){var f=a.value;return f=f.replace(new RegExp(c.value,e?e.value:""),d.value),new b(a.quote||"",f,a.escaped)},"%":function(a){for(var c=Array.prototype.slice.call(arguments,1),d=a.value,e=0;c.length>e;e++)d=d.replace(/%[sda]/i,function(a){var b=a.match(/s/i)?c[e].value:c[e].toCSS();return a.match(/[A-Z]$/)?encodeURIComponent(b):b});return d=d.replace(/%%/g,"%"),new b(a.quote||"",d,a.escaped)}})},{"../tree/anonymous":43,"../tree/javascript":60,"../tree/quoted":70,"./function-registry":21}],26:[function(a,b){b.exports=function(){var b=a("../tree/dimension"),c=a("../tree/color"),d=a("../tree/expression"),e=a("../tree/quoted"),f=a("../tree/url"),g=a("./function-registry");g.add("svg-gradient",function(a){function g(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&g();var h,i,j,k,l,m,n,o=Array.prototype.slice.call(arguments,1),p="linear",q='x="0" y="0" width="1" height="1"',r={compress:!1},s=a.toCSS(r);switch(s){case"to bottom":h='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":h='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":h='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":h='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":p="radial",h='cx="50%" cy="50%" r="75%"',q='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(i='<?xml version="1.0" ?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none"><'+p+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+h+">",j=0;o.length>j;j+=1)o[j]instanceof d?(k=o[j].value[0],l=o[j].value[1]):(k=o[j],l=void 0),k instanceof c&&((0===j||j+1===o.length)&&void 0===l||l instanceof b)||g(),m=l?l.toCSS(r):0===j?"0%":"100%",n=k.alpha,i+='<stop offset="'+m+'" stop-color="'+k.toRGB()+'"'+(1>n?' stop-opacity="'+n+'"':"")+"/>";return i+="</"+p+"Gradient><rect "+q+' fill="url(#gradient)" /></svg>',i=encodeURIComponent(i),i="data:image/svg+xml,"+i,new f(new e("'"+i+"'",i,!1,this.index,this.currentFileInfo),this.index,this.currentFileInfo)})}},{"../tree/color":47,"../tree/dimension":53,"../tree/expression":56,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],27:[function(a){var b=a("../tree/keyword"),c=a("../tree/detached-ruleset"),d=a("../tree/dimension"),e=a("../tree/color"),f=a("../tree/quoted"),g=a("../tree/anonymous"),h=a("../tree/url"),i=a("../tree/operation"),j=a("./function-registry"),k=function(a,c){return a instanceof c?b.True:b.False},l=function(a,c){if(void 0===c)throw{type:"Argument",message:"missing the required second argument to isunit."};
+if(c="string"==typeof c.value?c.value:c,"string"!=typeof c)throw{type:"Argument",message:"Second argument to isunit should be a unit or a string."};return a instanceof d&&a.unit.is(c)?b.True:b.False},m=function(a){var b=Array.isArray(a.value)?a.value:Array(a);return b.filter(function(a){return"Comment"===a.type?!1:!0})};j.addMultiple({isruleset:function(a){return k(a,c)},iscolor:function(a){return k(a,e)},isnumber:function(a){return k(a,d)},isstring:function(a){return k(a,f)},iskeyword:function(a){return k(a,b)},isurl:function(a){return k(a,h)},ispixel:function(a){return l(a,"px")},ispercentage:function(a){return l(a,"%")},isem:function(a){return l(a,"em")},isunit:l,unit:function(a,c){if(!(a instanceof d))throw{type:"Argument",message:"the first argument to unit must be a number"+(a instanceof i?". Have you forgotten parenthesis?":"")};return c=c?c instanceof b?c.value:c.toCSS():"",new d(a.value,c)},"get-unit":function(a){return new g(a.unit)},extract:function(a,b){return b=b.value-1,m(a)[b]},length:function(a){return new d(m(a).length)}})},{"../tree/anonymous":43,"../tree/color":47,"../tree/detached-ruleset":52,"../tree/dimension":53,"../tree/keyword":62,"../tree/operation":68,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],28:[function(a,b){var c=a("./contexts"),d=a("./parser/parser");b.exports=function(a){var b=function(a,b){this.rootFilename=b.filename,this.paths=a.paths||[],this.contents={},this.contentsIgnoredChars={},this.mime=a.mime,this.error=null,this.context=a,this.queue=[],this.files={}};return b.prototype.push=function(b,e,f,g,h){var i=this;this.queue.push(b);var j=function(a,c,d){i.queue.splice(i.queue.indexOf(b),1);var e=d===i.rootFilename;g.optional&&a?h(null,{rules:[]},!1,null):(i.files[d]=c,a&&!i.error&&(i.error=a),h(a,c,e,d))},k={relativeUrls:this.context.relativeUrls,entryPath:f.entryPath,rootpath:f.rootpath,rootFilename:f.rootFilename},l=a.getFileManager(b,f.currentDirectory,this.context,a);if(!l)return void j({message:"Could not find a file-manager for "+b});e&&(b=l.tryAppendLessExtension(b));var m=function(a){var b=a.filename,e=a.contents.replace(/^\uFEFF/,"");k.currentDirectory=l.getPath(b),k.relativeUrls&&(k.rootpath=l.join(i.context.rootpath||"",l.pathDiff(k.currentDirectory,k.entryPath)),!l.isPathAbsolute(k.rootpath)&&l.alwaysMakePathsAbsolute()&&(k.rootpath=l.join(k.entryPath,k.rootpath))),k.filename=b;var h=new c.Parse(i.context);h.processImports=!1,i.contents[b]=e,(f.reference||g.reference)&&(k.reference=!0),g.inline?j(null,e,b):new d(h,i,k).parse(e,function(a,c){j(a,c,b)})},n=l.loadFile(b,f.currentDirectory,this.context,a,function(a,b){a?j(a):m(b)});n&&n.then(m,j)},b}},{"./contexts":10,"./parser/parser":36}],29:[function(a,b){b.exports=function(b,c){var d,e,f,g,h,i={version:[2,4,0],data:a("./data"),tree:a("./tree"),Environment:h=a("./environment/environment"),AbstractFileManager:a("./environment/abstract-file-manager"),environment:b=new h(b,c),visitors:a("./visitors"),Parser:a("./parser/parser"),functions:a("./functions")(b),contexts:a("./contexts"),SourceMapOutput:d=a("./source-map-output")(b),SourceMapBuilder:e=a("./source-map-builder")(d,b),ParseTree:f=a("./parse-tree")(e),ImportManager:g=a("./import-manager")(b),render:a("./render")(b,f,g),parse:a("./parse")(b,f,g),LessError:a("./less-error"),transformTree:a("./transform-tree"),utils:a("./utils"),PluginManager:a("./plugin-manager"),logger:a("./logger")};return i}},{"./contexts":10,"./data":12,"./environment/abstract-file-manager":14,"./environment/environment":15,"./functions":22,"./import-manager":28,"./less-error":30,"./logger":31,"./parse":33,"./parse-tree":32,"./parser/parser":36,"./plugin-manager":37,"./render":38,"./source-map-builder":39,"./source-map-output":40,"./transform-tree":41,"./tree":59,"./utils":80,"./visitors":84}],30:[function(a,b){var c=a("./utils"),d=b.exports=function(a,b,d){Error.call(this);var e=a.filename||d;if(b&&e){var f=b.contents[e],g=c.getLocation(a.index,f),h=g.line,i=g.column,j=a.call&&c.getLocation(a.call,f).line,k=f.split("\n");this.type=a.type||"Syntax",this.filename=e,this.index=a.index,this.line="number"==typeof h?h+1:null,this.callLine=j+1,this.callExtract=k[j],this.column=i,this.extract=[k[h-1],k[h],k[h+1]]}this.message=a.message,this.stack=a.stack};if("undefined"==typeof Object.create){var e=function(){};e.prototype=Error.prototype,d.prototype=new e}else d.prototype=Object.create(Error.prototype);d.prototype.constructor=d},{"./utils":80}],31:[function(a,b){b.exports={error:function(a){this._fireEvent("error",a)},warn:function(a){this._fireEvent("warn",a)},info:function(a){this._fireEvent("info",a)},debug:function(a){this._fireEvent("debug",a)},addListener:function(a){this._listeners.push(a)},removeListener:function(a){for(var b=0;this._listeners.length>b;b++)if(this._listeners[b]===a)return void this._listeners.splice(b,1)},_fireEvent:function(a,b){for(var c=0;this._listeners.length>c;c++){var d=this._listeners[c][a];d&&d(b)}},_listeners:[]}},{}],32:[function(a,b){var c=a("./less-error"),d=a("./transform-tree"),e=a("./logger");b.exports=function(a){var b=function(a,b){this.root=a,this.imports=b};return b.prototype.toCSS=function(b){var f,g,h={};try{f=d(this.root,b)}catch(i){throw new c(i,this.imports)}try{var j=Boolean(b.compress);j&&e.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");var k={compress:j,dumpLineNumbers:b.dumpLineNumbers,strictUnits:Boolean(b.strictUnits),numPrecision:8};b.sourceMap?(g=new a(b.sourceMap),h.css=g.toCSS(f,k,this.imports)):h.css=f.toCSS(k)}catch(i){throw new c(i,this.imports)}if(b.pluginManager)for(var l=b.pluginManager.getPostProcessors(),m=0;l.length>m;m++)h.css=l[m].process(h.css,{sourceMap:g,options:b,imports:this.imports});b.sourceMap&&(h.map=g.getExternalSourceMap()),h.imports=[];for(var n in this.imports.files)this.imports.files.hasOwnProperty(n)&&n!==this.imports.rootFilename&&h.imports.push(n);return h},b}},{"./less-error":30,"./logger":31,"./transform-tree":41}],33:[function(a,b){var c,d=a("./contexts"),e=a("./parser/parser"),f=a("./plugin-manager");b.exports=function(b,g,h){var i=function(b,g,j){if(g=g||{},"function"==typeof g&&(j=g,g={}),!j){c||(c="undefined"==typeof Promise?a("promise"):Promise);var k=this;return new c(function(a,c){i.call(k,b,g,function(b,d){b?c(b):a(d)})})}var l,m,n=new f(this);if(n.addPlugins(g.plugins),g.pluginManager=n,l=new d.Parse(g),g.rootFileInfo)m=g.rootFileInfo;else{var o=g.filename||"input",p=o.replace(/[^\/\\]*$/,"");m={filename:o,relativeUrls:l.relativeUrls,rootpath:l.rootpath||"",currentDirectory:p,entryPath:p,rootFilename:o},m.rootpath&&"/"!==m.rootpath.slice(-1)&&(m.rootpath+="/")}var q=new h(l,m);new e(l,q,m).parse(b,function(a,b){return a?j(a):void j(null,b,q,g)},g)};return i}},{"./contexts":10,"./parser/parser":36,"./plugin-manager":37,promise:void 0}],34:[function(a,b){b.exports=function(a,b){function c(b){var c=h-q;512>c&&!b||!c||(p.push(a.slice(q,h+1)),q=h+1)}var d,e,f,g,h,i,j,k,l,m=a.length,n=0,o=0,p=[],q=0;for(h=0;m>h;h++)if(j=a.charCodeAt(h),!(j>=97&&122>=j||34>j))switch(j){case 40:o++,e=h;continue;case 41:if(--o<0)return b("missing opening `(`",h);continue;case 59:o||c();continue;case 123:n++,d=h;continue;case 125:if(--n<0)return b("missing opening `{`",h);n||o||c();continue;case 92:if(m-1>h){h++;continue}return b("unescaped `\\`",h);case 34:case 39:case 96:for(l=0,i=h,h+=1;m>h;h++)if(k=a.charCodeAt(h),!(k>96)){if(k==j){l=1;break}if(92==k){if(h==m-1)return b("unescaped `\\`",h);h++}}if(l)continue;return b("unmatched `"+String.fromCharCode(j)+"`",i);case 47:if(o||h==m-1)continue;if(k=a.charCodeAt(h+1),47==k)for(h+=2;m>h&&(k=a.charCodeAt(h),!(13>=k)||10!=k&&13!=k);h++);else if(42==k){for(f=i=h,h+=2;m-1>h&&(k=a.charCodeAt(h),125==k&&(g=h),42!=k||47!=a.charCodeAt(h+1));h++);if(h==m-1)return b("missing closing `*/`",i);h++}continue;case 42:if(m-1>h&&47==a.charCodeAt(h+1))return b("unmatched `/*`",h);continue}return 0!==n?f>d&&g>f?b("missing closing `}` or `*/`",d):b("missing closing `}`",d):0!==o?b("missing closing `)`",e):(c(!0),p)}},{}],35:[function(a,b){var c=a("./chunker");b.exports=function(){function a(){k.i>i&&(h=h.slice(k.i-i),i=k.i)}var b,d,e,f,g,h,i,j=[],k={};k.save=function(){i=k.i,j.push({current:h,i:k.i,j:d})},k.restore=function(a){(k.i>e||k.i===e&&a&&!f)&&(e=k.i,f=a);var b=j.pop();h=b.current,i=k.i=b.i,d=b.j},k.forget=function(){j.pop()},k.isWhitespace=function(a){var c=k.i+(a||0),d=b.charCodeAt(c);return d===l||d===o||d===m||d===n},k.$=function(c){var d,e,f=typeof c;return"string"===f?b.charAt(k.i)!==c?null:(t(1),c):(a(),(d=c.exec(h))?(e=d[0].length,t(e),"string"==typeof d?d:1===d.length?d[0]:d):null)},k.$re=function(a){k.i>i&&(h=h.slice(k.i-i),i=k.i);var b=a.exec(h);return b?(t(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null},k.$char=function(a){return b.charAt(k.i)!==a?null:(t(1),a)};var l=32,m=9,n=10,o=13,p=43,q=44,r=47,s=57;k.autoCommentAbsorb=!0,k.commentStore=[],k.finished=!1;var t=function(a){for(var c,e,f,j=k.i,p=d,q=k.i-i,s=k.i+h.length-q,u=k.i+=a,v=b;s>k.i;k.i++){if(c=v.charCodeAt(k.i),k.autoCommentAbsorb&&c===r){if(e=v.charAt(k.i+1),"/"===e){f={index:k.i,isLineComment:!0};var w=v.indexOf("\n",k.i+1);0>w&&(w=s),k.i=w,f.text=v.substr(f.i,k.i-f.i),k.commentStore.push(f);continue}if("*"===e){var x=v.substr(k.i),y=x.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//);if(y){f={index:k.i,text:y[0],isLineComment:!1},k.i+=f.text.length-1,k.commentStore.push(f);continue}}break}if(c!==l&&c!==n&&c!==m&&c!==o)break}if(h=h.slice(a+k.i-u+q),i=k.i,!h.length){if(g.length-1>d)return h=g[++d],t(0),!0;k.finished=!0}return j!==k.i||p!==d};return k.peek=function(a){return"string"==typeof a?b.charAt(k.i)===a:a.test(h)},k.peekChar=function(a){return b.charAt(k.i)===a},k.currentChar=function(){return b.charAt(k.i)},k.getInput=function(){return b},k.peekNotNumeric=function(){var a=b.charCodeAt(k.i);return a>s||p>a||a===r||a===q},k.start=function(a,f,j){b=a,k.i=d=i=e=0,g=f?c(a,j):[a],h=g[0],t(0)},k.end=function(){var a,c=k.i>=b.length;return e>k.i&&(a=f,k.i=e),{isFinished:c,furthest:k.i,furthestPossibleErrorMessage:a,furthestReachedEnd:k.i>=b.length-1,furthestChar:b[k.i]}},k}},{"./chunker":34}],36:[function(a,b){var c=a("../less-error"),d=a("../tree"),e=a("../visitors"),f=a("./parser-input"),g=a("../utils"),h=function i(a,b,h){function j(a,b){var c="[object Function]"===Object.prototype.toString.call(a)?a.call(n):o.$(a);return c?c:void l(b||("string"==typeof a?"expected '"+a+"' got '"+o.currentChar()+"'":"unexpected token"))}function k(a,b){return o.$char(a)?a:void l(b||"expected '"+a+"' got '"+o.currentChar()+"'")}function l(a,d){throw new c({index:o.i,filename:h.filename,type:d||"Syntax",message:a},b)}function m(a){var b=h.filename;return{lineNumber:g.getLocation(a,o.getInput()).line+1,fileName:b}}var n,o=f();return{parse:function(f,g,j){var k,l,m,n,p=null,q="";if(l=j&&j.globalVars?i.serializeVars(j.globalVars)+"\n":"",m=j&&j.modifyVars?"\n"+i.serializeVars(j.modifyVars):"",a.pluginManager)for(var r=a.pluginManager.getPreProcessors(),s=0;r.length>s;s++)f=r[s].process(f,{context:a,imports:b,fileInfo:h});(l||j&&j.banner)&&(q=(j&&j.banner?j.banner:"")+l,n=b.contentsIgnoredChars,n[h.filename]=n[h.filename]||0,n[h.filename]+=q.length),f=f.replace(/\r\n?/g,"\n"),f=q+f.replace(/^\uFEFF/,"")+m,b.contents[h.filename]=f;try{o.start(f,a.chunkInput,function(a,d){throw c({index:d,type:"Parse",message:a,filename:h.filename},b)}),k=new d.Ruleset(null,this.parsers.primary()),k.root=!0,k.firstRoot=!0}catch(t){return g(new c(t,b,h.filename))}var u=o.end();if(!u.isFinished){var v=u.furthestPossibleErrorMessage;v||(v="Unrecognised input","}"===u.furthestChar?v+=". Possibly missing opening '{'":")"===u.furthestChar?v+=". Possibly missing opening '('":u.furthestReachedEnd&&(v+=". Possibly missing something")),p=new c({type:"Parse",message:v,index:u.furthest,filename:h.filename},b)}var w=function(a){return a=p||a||b.error,a?(a instanceof c||(a=new c(a,b,h.filename)),g(a)):g(null,k)};return a.processImports===!1?w():void new e.ImportVisitor(b,w).run(k)},parsers:n={primary:function(){for(var a,b=this.mixin,c=[];;){for(;;){if(a=this.comment(),!a)break;c.push(a)}if(o.finished)break;if(o.peek("}"))break;if(a=this.extendRule())c=c.concat(a);else if(a=b.definition()||this.rule()||this.ruleset()||b.call()||this.rulesetCall()||this.directive())c.push(a);else if(!o.$re(/^[\s\n]+/)&&!o.$re(/^;+/))break}return c},comment:function(){if(o.commentStore.length){var a=o.commentStore.shift();return new d.Comment(a.text,a.isLineComment,a.index,h)}},entities:{quoted:function(){var a,b=o.i;return a=o.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/),a?new d.Quoted(a[2],a[3]||a[4],Boolean(a[1]),b,h):void 0},keyword:function(){var a=o.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);return a?d.Color.fromKeyword(a)||new d.Keyword(a):void 0},call:function(){var a,b,c,e,f=o.i;if(!o.peek(/^url\(/i))return o.save(),(a=o.$re(/^([\w-]+|%|progid:[\w\.]+)\(/))?(a=a[1],b=a.toLowerCase(),"alpha"===b&&(e=n.alpha())?e:(c=this.arguments(),o.$char(")")?(o.forget(),new d.Call(a,c,f,h)):void o.restore("Could not parse call arguments or missing ')'"))):void o.forget()},arguments:function(){for(var a,b=[];;){if(a=this.assignment()||n.expression(),!a)break;if(b.push(a),!o.$char(","))break}return b},literal:function(){return this.dimension()||this.color()||this.quoted()||this.unicodeDescriptor()},assignment:function(){var a,b;return a=o.$re(/^\w+(?=\s?=)/i),a&&o.$char("=")?(b=n.entity(),b?new d.Assignment(a,b):void 0):void 0},url:function(){var a,b=o.i;return o.autoCommentAbsorb=!1,"u"===o.currentChar()&&o.$re(/^url\(/)?(a=this.quoted()||this.variable()||o.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",o.autoCommentAbsorb=!0,k(")"),new d.URL(null!=a.value||a instanceof d.Variable?a:new d.Anonymous(a),b,h)):void(o.autoCommentAbsorb=!0)},variable:function(){var a,b=o.i;return"@"===o.currentChar()&&(a=o.$re(/^@@?[\w-]+/))?new d.Variable(a,b,h):void 0},variableCurly:function(){var a,b=o.i;return"@"===o.currentChar()&&(a=o.$re(/^@\{([\w-]+)\}/))?new d.Variable("@"+a[1],b,h):void 0},color:function(){var a;if("#"===o.currentChar()&&(a=o.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))){var b=a.input.match(/^#([\w]+).*/);return b=b[1],b.match(/^[A-Fa-f0-9]+$/)||l("Invalid HEX color code"),new d.Color(a[1])}},dimension:function(){if(!o.peekNotNumeric()){var a=o.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i);return a?new d.Dimension(a[1],a[2]):void 0}},unicodeDescriptor:function(){var a;return a=o.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new d.UnicodeDescriptor(a[0]):void 0},javascript:function(){var a,b=o.i;return a=o.$re(/^(~)?`([^`]*)`/),a?new d.JavaScript(a[2],Boolean(a[1]),b,h):void 0}},variable:function(){var a;return"@"===o.currentChar()&&(a=o.$re(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===o.currentChar()&&(a=o.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new d.RulesetCall(a[1]):void 0},extend:function(a){var b,c,e,f,g,h=o.i;if(o.$re(a?/^&:extend\(/:/^:extend\(/)){do{for(e=null,b=null;!(e=o.$re(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];e=e&&e[1],b||l("Missing target selector for :extend()."),g=new d.Extend(new d.Selector(b),e,h),f?f.push(g):f=[g]}while(o.$char(","));return j(/^\)/),a&&j(/^;/),f}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var a,b,c,e,f,g,i=o.currentChar(),j=!1,l=o.i;if("."===i||"#"===i){for(o.save();;){if(a=o.i,e=o.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!e)break;c=new d.Element(f,e,a,h),b?b.push(c):b=[c],f=o.$char(">")}return b&&(o.$char("(")&&(g=this.args(!0).args,k(")")),n.important()&&(j=!0),n.end())?(o.forget(),new d.mixin.Call(b,g,l,h,j)):void o.restore()}},args:function(a){var b,c,e,f,g,h,i=n.entities,j={args:null,variadic:!1},k=[],m=[],p=[];for(o.save();;){if(a)h=n.detachedRuleset()||n.expression();else{if(o.commentStore.length=0,"."===o.currentChar()&&o.$re(/^\.{3}/)){j.variadic=!0,o.$char(";")&&!b&&(b=!0),(b?m:p).push({variadic:!0});break}h=i.variable()||i.literal()||i.keyword()}if(!h)break;f=null,h.throwAwayComments&&h.throwAwayComments(),g=h;var q=null;if(a?h.value&&1==h.value.length&&(q=h.value[0]):q=h,q&&q instanceof d.Variable)if(o.$char(":")){if(k.length>0&&(b&&l("Cannot mix ; and , as delimiter types"),c=!0),g=a&&n.detachedRuleset()||n.expression(),!g){if(!a)return o.restore(),j.args=[],j;l("could not understand value for named argument")}f=e=q.name}else{if(!a&&o.$re(/^\.{3}/)){j.variadic=!0,o.$char(";")&&!b&&(b=!0),(b?m:p).push({name:h.name,variadic:!0});break}a||(e=f=q.name,g=null)}g&&k.push(g),p.push({name:f,value:g}),o.$char(",")||(o.$char(";")||b)&&(c&&l("Cannot mix ; and , as delimiter types"),b=!0,k.length>1&&(g=new d.Value(k)),m.push({name:e,value:g}),e=null,k=[],c=!1)}return o.forget(),j.args=b?m:p,j},definition:function(){var a,b,c,e,f=[],g=!1;if(!("."!==o.currentChar()&&"#"!==o.currentChar()||o.peek(/^[^{]*\}/)))if(o.save(),b=o.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var h=this.args(!1);if(f=h.args,g=h.variadic,!o.$char(")"))return void o.restore("Missing closing ')'");if(o.commentStore.length=0,o.$re(/^when/)&&(e=j(n.conditions,"expected condition")),c=n.block())return o.forget(),new d.mixin.Definition(a,f,c,e,g);o.restore()}else o.forget()}},entity:function(){var a=this.entities;return this.comment()||a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()},end:function(){return o.$char(";")||o.peek("}")},alpha:function(){var a;if(o.$re(/^opacity=/i))return a=o.$re(/^\d+/),a||(a=j(this.entities.variable,"Could not parse alpha")),k(")"),new d.Alpha(a)},element:function(){var a,b,c,e=o.i;return b=this.combinator(),a=o.$re(/^(?:\d+\.\d+|\d+)%/)||o.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||o.$char("*")||o.$char("&")||this.attribute()||o.$re(/^\([^&()@]+\)/)||o.$re(/^[\.#:](?=@)/)||this.entities.variableCurly(),a||(o.save(),o.$char("(")?(c=this.selector())&&o.$char(")")?(a=new d.Paren(c),o.forget()):o.restore("Missing closing ')'"):o.forget()),a?new d.Element(b,a,e,h):void 0},combinator:function(){var a=o.currentChar();if("/"===a){o.save();var b=o.$re(/^\/[a-z]+\//i);if(b)return o.forget(),new d.Combinator(b);o.restore()}if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(o.i++,"^"===a&&"^"===o.currentChar()&&(a="^^",o.i++);o.isWhitespace();)o.i++;return new d.Combinator(a)}return new d.Combinator(o.isWhitespace(-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(a){for(var b,c,e,f,g,i,k,m=o.i;(a&&(c=this.extend())||a&&(i=o.$re(/^when/))||(f=this.element()))&&(i?k=j(this.conditions,"expected condition"):k?l("CSS guard can only be used at the end of selector"):c?g=g?g.concat(c):c:(g&&l("Extend can only be used at the end of selector"),e=o.currentChar(),b?b.push(f):b=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return b?new d.Selector(b,g,k,m,h):void(g&&l("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(o.$char("[")){var a,b,c,e=this.entities;return(a=e.variableCurly())||(a=j(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=o.$re(/^[|~*$^]?=/),c&&(b=e.quoted()||o.$re(/^[0-9]+%/)||o.$re(/^[\w-]+/)||e.variableCurly()),k("]"),new d.Attribute(a,c,b)}},block:function(){var a;return o.$char("{")&&(a=this.primary())&&o.$char("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new d.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new d.DetachedRuleset(a):void 0},ruleset:function(){var b,c,e,f;for(o.save(),a.dumpLineNumbers&&(f=m(o.i));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],o.commentStore.length=0,c.condition&&b.length>1&&l("Guards are only currently allowed on a single selector."),!o.$char(","))break;c.condition&&l("Guards are only currently allowed on a single selector."),o.commentStore.length=0}if(b&&(e=this.block())){o.forget();var g=new d.Ruleset(b,e,a.strictImports);return a.dumpLineNumbers&&(g.debugInfo=f),g}o.restore()},rule:function(b){var c,e,f,g,i,j=o.i,k=o.currentChar();if("."!==k&&"#"!==k&&"&"!==k)if(o.save(),c=this.variable()||this.ruleProperty()){if(i="string"==typeof c,i&&(e=this.detachedRuleset()),o.commentStore.length=0,!e){g=!i&&c.pop().value;var l=!b&&(a.compress||i);if(l&&(e=this.value()),!e&&(e=this.anonymousValue()))return o.forget(),new d.Rule(c,e,!1,g,j,h);l||e||(e=this.value()),f=this.important()}if(e&&this.end())return o.forget(),new d.Rule(c,e,f,g,j,h);if(o.restore(),e&&!b)return this.rule(!0)}else o.forget()},anonymousValue:function(){var a=o.$re(/^([^@+\/'"*`(;{}-]*);/);return a?new d.Anonymous(a[1]):void 0},"import":function(){var a,b,c=o.i,e=o.$re(/^@import?\s+/);if(e){var f=(e?this.importOptions():null)||{};if(a=this.entities.quoted()||this.entities.url())return b=this.mediaFeatures(),o.$(";")||(o.i=c,l("missing semi-colon or unrecognised media features on import")),b=b&&new d.Value(b),new d.Import(a,b,f,c,h);o.i=c,l("malformed import statement")}},importOptions:function(){var a,b,c,d={};if(!o.$char("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!o.$char(","))break}while(a);return k(")"),d},importOption:function(){var a=o.$re(/^(less|css|multiple|once|inline|reference|optional)/);return a?a[1]:void 0},mediaFeature:function(){var a,b,c=this.entities,e=[];o.save();do if(a=c.keyword()||c.variable())e.push(a);else if(o.$char("(")){if(b=this.property(),a=this.value(),!o.$char(")"))return o.restore("Missing closing ')'"),null;if(b&&a)e.push(new d.Paren(new d.Rule(b,a,null,null,o.i,h,!0)));else{if(!a)return o.restore("badly formed media feature definition"),null;e.push(new d.Paren(a))}}while(a);return o.forget(),e.length>0?new d.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!o.$char(","))break}else if(a=b.variable(),a&&(c.push(a),!o.$char(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,e,f;return a.dumpLineNumbers&&(f=m(o.i)),o.$re(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(e=new d.Media(c,b,o.i,h),a.dumpLineNumbers&&(e.debugInfo=f),e):void 0},directive:function(){var b,c,e,f,g,i,j,k=o.i,n=!0;if("@"===o.currentChar()){if(c=this["import"]()||this.media())return c;if(o.save(),b=o.$re(/^@[a-z-]+/)){switch(f=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(f="@"+b.slice(b.indexOf("-",2)+1)),f){case"@counter-style":g=!0,n=!0;break;case"@charset":g=!0,n=!1;break;case"@namespace":i=!0,n=!1;break;case"@keyframes":g=!0;break;case"@host":case"@page":case"@document":case"@supports":j=!0}return o.commentStore.length=0,g?(c=this.entity(),c||l("expected "+b+" identifier")):i?(c=this.expression(),c||l("expected "+b+" expression")):j&&(c=(o.$re(/^[^{;]+/)||"").trim(),c&&(c=new d.Anonymous(c))),n&&(e=this.blockRuleset()),e||!n&&c&&o.$char(";")?(o.forget(),new d.Directive(b,c,e,k,h,a.dumpLineNumbers?m(k):null)):void o.restore("directive options not recognised")}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!o.$char(",")))break;while(a);return b.length>0?new d.Value(b):void 0},important:function(){return"!"===o.currentChar()?o.$re(/^! *important/):void 0},sub:function(){var a,b;return o.save(),o.$char("(")?(a=this.addition(),a&&o.$char(")")?(o.forget(),b=new d.Expression([a]),b.parens=!0,b):void o.restore("Expected ')'")):void o.restore()},multiplication:function(){var a,b,c,e,f;if(a=this.operand()){for(f=o.isWhitespace(-1);;){if(o.peek(/^\/[*\/]/))break;if(o.save(),c=o.$char("/")||o.$char("*"),!c){o.forget();break}if(b=this.operand(),!b){o.restore();break}o.forget(),a.parensInOp=!0,b.parensInOp=!0,e=new d.Operation(c,[e||a,b],f),f=o.isWhitespace(-1)}return e||a}},addition:function(){var a,b,c,e,f;if(a=this.multiplication()){for(f=o.isWhitespace(-1);;){if(c=o.$re(/^[-+]\s+/)||!f&&(o.$char("+")||o.$char("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,e=new d.Operation(c,[e||a,b],f),f=o.isWhitespace(-1)}return e||a}},conditions:function(){var a,b,c,e=o.i;if(a=this.condition()){for(;;){if(!o.peek(/^,\s*(not\s*)?\(/)||!o.$char(","))break;if(b=this.condition(),!b)break;c=new d.Condition("or",c||a,b,e)}return c||a}},condition:function(){var a,b,c,e,f=this.entities,g=o.i,h=!1;return o.$re(/^not/)&&(h=!0),k("("),a=this.addition()||f.keyword()||f.quoted(),a?(e=o.$re(/^(?:>=|<=|=<|[<=>])/),e?(b=this.addition()||f.keyword()||f.quoted(),b?c=new d.Condition(e,a,b,g,h):l("expected expression")):c=new d.Condition("=",a,new d.Keyword("true"),g,h),k(")"),o.$re(/^and/)?new d.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities;o.peek(/^-[@\(]/)&&(a=o.$char("-"));var c=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(c.parensInOp=!0,c=new d.Negative(c)),c},expression:function(){var a,b,c=[];do a=this.comment(),a?c.push(a):(a=this.addition()||this.entity(),a&&(c.push(a),o.peek(/^\/[\/*]/)||(b=o.$char("/"),b&&c.push(new d.Anonymous(b)))));while(a);return c.length>0?new d.Expression(c):void 0},property:function(){var a=o.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function a(a){var b=o.i,c=o.$re(a);return c?(f.push(b),e.push(c[1])):void 0}var b,c,e=[],f=[];for(o.save(),a(/^(\*?)/);;)if(!a(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/))break;if(e.length>1&&a(/^((?:\+_|\+)?)\s*:/)){for(o.forget(),""===e[0]&&(e.shift(),f.shift()),c=0;e.length>c;c++)b=e[c],e[c]="@"!==b.charAt(0)?new d.Keyword(b):new d.Variable("@"+b.slice(2,-1),f[c],h);return e}o.restore()}}}};h.serializeVars=function(a){var b="";for(var c in a)if(Object.hasOwnProperty.call(a,c)){var d=a[c];b+=("@"===c[0]?"":"@")+c+": "+d+(";"===String(d).slice(-1)?"":";")}return b},b.exports=h},{"../less-error":30,"../tree":59,"../utils":80,"../visitors":84,"./parser-input":35}],37:[function(a,b){var c=function(a){this.less=a,this.visitors=[],this.preProcessors=[],this.postProcessors=[],this.installedPlugins=[],this.fileManagers=[]};c.prototype.addPlugins=function(a){if(a)for(var b=0;a.length>b;b++)this.addPlugin(a[b])},c.prototype.addPlugin=function(a){this.installedPlugins.push(a),a.install(this.less,this)},c.prototype.addVisitor=function(a){this.visitors.push(a)},c.prototype.addPreProcessor=function(a,b){var c;for(c=0;this.preProcessors.length>c&&!(this.preProcessors[c].priority>=b);c++);this.preProcessors.splice(c,0,{preProcessor:a,priority:b})},c.prototype.addPostProcessor=function(a,b){var c;for(c=0;this.postProcessors.length>c&&!(this.postProcessors[c].priority>=b);c++);this.postProcessors.splice(c,0,{postProcessor:a,priority:b})},c.prototype.addFileManager=function(a){this.fileManagers.push(a)},c.prototype.getPreProcessors=function(){for(var a=[],b=0;this.preProcessors.length>b;b++)a.push(this.preProcessors[b].preProcessor);return a},c.prototype.getPostProcessors=function(){for(var a=[],b=0;this.postProcessors.length>b;b++)a.push(this.postProcessors[b].postProcessor);return a},c.prototype.getVisitors=function(){return this.visitors},c.prototype.getFileManagers=function(){return this.fileManagers},b.exports=c},{}],38:[function(a,b){var c;b.exports=function(b,d){var e=function(b,f,g){if("function"==typeof f&&(g=f,f={}),!g){c||(c="undefined"==typeof Promise?a("promise"):Promise);var h=this;return new c(function(a,c){e.call(h,b,f,function(b,d){b?c(b):a(d)})})}this.parse(b,f,function(a,b,c,e){if(a)return g(a);var f;try{var h=new d(b,c);f=h.toCSS(e)}catch(a){return g(a)}g(null,f)})};return e}},{promise:void 0}],39:[function(a,b){b.exports=function(a,b){var c=function(a){this.options=a};return c.prototype.toCSS=function(b,c,d){var e=new a({contentsIgnoredCharsMap:d.contentsIgnoredChars,rootNode:b,contentsMap:d.contents,sourceMapFilename:this.options.sourceMapFilename,sourceMapURL:this.options.sourceMapURL,outputFilename:this.options.sourceMapOutputFilename,sourceMapBasepath:this.options.sourceMapBasepath,sourceMapRootpath:this.options.sourceMapRootpath,outputSourceFiles:this.options.outputSourceFiles,sourceMapGenerator:this.options.sourceMapGenerator,sourceMapFileInline:this.options.sourceMapFileInline}),f=e.toCSS(c);return this.sourceMap=e.sourceMap,this.sourceMapURL=e.sourceMapURL,this.options.sourceMapInputFilename&&(this.sourceMapInputFilename=e.normalizeFilename(this.options.sourceMapInputFilename)),f+this.getCSSAppendage()},c.prototype.getCSSAppendage=function(){var a=this.sourceMapURL;if(this.options.sourceMapFileInline){if(void 0===this.sourceMap)return"";a="data:application/json;base64,"+b.encodeBase64(this.sourceMap)}return a?"/*# sourceMappingURL="+a+" */":""},c.prototype.getExternalSourceMap=function(){return this.sourceMap},c.prototype.setExternalSourceMap=function(a){this.sourceMap=a},c.prototype.isInline=function(){return this.options.sourceMapFileInline},c.prototype.getSourceMapURL=function(){return this.sourceMapURL},c.prototype.getOutputFilename=function(){return this.options.sourceMapOutputFilename},c.prototype.getInputFilename=function(){return this.sourceMapInputFilename},c}},{}],40:[function(a,b){b.exports=function(a){var b=function(b){this._css=[],this._rootNode=b.rootNode,this._contentsMap=b.contentsMap,this._contentsIgnoredCharsMap=b.contentsIgnoredCharsMap,b.sourceMapFilename&&(this._sourceMapFilename=b.sourceMapFilename.replace(/\\/g,"/")),this._outputFilename=b.outputFilename,this.sourceMapURL=b.sourceMapURL,b.sourceMapBasepath&&(this._sourceMapBasepath=b.sourceMapBasepath.replace(/\\/g,"/")),b.sourceMapRootpath?(this._sourceMapRootpath=b.sourceMapRootpath.replace(/\\/g,"/"),"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/")):this._sourceMapRootpath="",this._outputSourceFiles=b.outputSourceFiles,this._sourceMapGeneratorConstructor=a.getSourceMapGenerator(),this._lineNumber=0,this._column=0};return b.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},b.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;e.length>i;i++)this._sourceMapGenerator.addMapping({generated:{line:this._lineNumber+i+1,column:0===i?this._column:0},original:{line:f.length+i,column:0===i?h.length:0},source:this.normalizeFilename(b.filename)});else this._sourceMapGenerator.addMapping({generated:{line:this._lineNumber+1,column:this._column},original:{line:f.length,column:h.length},source:this.normalizeFilename(b.filename)});1===e.length?this._column+=g.length:(this._lineNumber+=e.length-1,this._column=g.length),this._css.push(a)}},b.prototype.isEmpty=function(){return 0===this._css.length},b.prototype.toCSS=function(a){if(this._sourceMapGenerator=new this._sourceMapGeneratorConstructor({file:this._outputFilename,sourceRoot:null}),this._outputSourceFiles)for(var b in this._contentsMap)if(this._contentsMap.hasOwnProperty(b)){var c=this._contentsMap[b];this._contentsIgnoredCharsMap[b]&&(c=c.slice(this._contentsIgnoredCharsMap[b])),this._sourceMapGenerator.setSourceContent(this.normalizeFilename(b),c)}if(this._rootNode.genCSS(a,this),this._css.length>0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this.sourceMapURL?d=this.sourceMapURL:this._sourceMapFilename&&(d=this._sourceMapFilename),this.sourceMapURL=d,this.sourceMap=e}return this._css.join("")},b}},{}],41:[function(a,b){var c=a("./contexts"),d=a("./visitors"),e=a("./tree");b.exports=function(a,b){b=b||{};var f,g=b.variables,h=new c.Eval(b);"object"!=typeof g||Array.isArray(g)||(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b])),new e.Rule("@"+a,b,!1,null,0)
+}),h.frames=[new e.Ruleset(null,g)]);var i,j=[],k=[new d.JoinSelectorVisitor,new d.ExtendVisitor,new d.ToCSSVisitor({compress:Boolean(b.compress)})];if(b.pluginManager){var l=b.pluginManager.getVisitors();for(i=0;l.length>i;i++){var m=l[i];m.isPreEvalVisitor?j.push(m):m.isPreVisitor?k.splice(0,0,m):k.push(m)}}for(i=0;j.length>i;i++)j[i].run(a);for(f=a.eval(h),i=0;k.length>i;i++)k[i].run(f);return f}},{"./contexts":10,"./tree":59,"./visitors":84}],42:[function(a,b){var c=a("./node"),d=function(a){this.value=a};d.prototype=new c,d.prototype.type="Alpha",d.prototype.accept=function(a){this.value=a.visit(this.value)},d.prototype.eval=function(a){return this.value.eval?new d(this.value.eval(a)):this},d.prototype.genCSS=function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},b.exports=d},{"./node":67}],43:[function(a,b){var c=a("./node"),d=function(a,b,c,d,e){this.value=a,this.index=b,this.mapLines=d,this.currentFileInfo=c,this.rulesetLike="undefined"==typeof e?!1:e};d.prototype=new c,d.prototype.type="Anonymous",d.prototype.eval=function(){return new d(this.value,this.index,this.currentFileInfo,this.mapLines,this.rulesetLike)},d.prototype.compare=function(a){return a.toCSS&&this.toCSS()===a.toCSS()?0:void 0},d.prototype.isRulesetLike=function(){return this.rulesetLike},d.prototype.genCSS=function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},b.exports=d},{"./node":67}],44:[function(a,b){var c=a("./node"),d=function(a,b){this.key=a,this.value=b};d.prototype=new c,d.prototype.type="Assignment",d.prototype.accept=function(a){this.value=a.visit(this.value)},d.prototype.eval=function(a){return this.value.eval?new d(this.key,this.value.eval(a)):this},d.prototype.genCSS=function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},b.exports=d},{"./node":67}],45:[function(a,b){var c=a("./node"),d=function(a,b,c){this.key=a,this.op=b,this.value=c};d.prototype=new c,d.prototype.type="Attribute",d.prototype.eval=function(a){return new d(this.key.eval?this.key.eval(a):this.key,this.op,this.value&&this.value.eval?this.value.eval(a):this.value)},d.prototype.genCSS=function(a,b){b.add(this.toCSS(a))},d.prototype.toCSS=function(a){var b=this.key.toCSS?this.key.toCSS(a):this.key;return this.op&&(b+=this.op,b+=this.value.toCSS?this.value.toCSS(a):this.value),"["+b+"]"},b.exports=d},{"./node":67}],46:[function(a,b){var c=a("./node"),d=a("../functions/function-caller"),e=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d};e.prototype=new c,e.prototype.type="Call",e.prototype.accept=function(a){this.args&&(this.args=a.visitArray(this.args))},e.prototype.eval=function(a){var b,c=this.args.map(function(b){return b.eval(a)}),f=new d(this.name,a,this.index,this.currentFileInfo);if(f.isValid())try{if(b=f.call(c),null!=b)return b}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new e(this.name,c,this.index,this.currentFileInfo)},e.prototype.genCSS=function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;this.args.length>c;c++)this.args[c].genCSS(a,b),this.args.length>c+1&&b.add(", ");b.add(")")},b.exports=e},{"../functions/function-caller":20,"./node":67}],47:[function(a,b){function c(a,b){return Math.min(Math.max(a,0),b)}function d(a){return"#"+a.map(function(a){return a=c(Math.round(a),255),(16>a?"0":"")+a.toString(16)}).join("")}var e=a("./node"),f=a("../data/colors"),g=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};g.prototype=new e,g.prototype.type="Color",g.prototype.luma=function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},g.prototype.genCSS=function(a,b){b.add(this.toCSS(a))},g.prototype.toCSS=function(a,b){var d,e,f=a&&a.compress&&!b;if(this.value)return this.value;if(e=this.fround(a,this.alpha),1>e)return"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(e,1)).join(","+(f?"":" "))+")";if(d=this.toRGB(),f){var g=d.split("");g[1]===g[2]&&g[3]===g[4]&&g[5]===g[6]&&(d="#"+g[1]+g[3]+g[5])}return d},g.prototype.operate=function(a,b,c){for(var d=[],e=this.alpha*(1-c.alpha)+c.alpha,f=0;3>f;f++)d[f]=this._operate(a,b,this.rgb[f],c.rgb[f]);return new g(d,e)},g.prototype.toRGB=function(){return d(this.rgb)},g.prototype.toHSL=function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},g.prototype.toHSV=function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},g.prototype.toARGB=function(){return d([255*this.alpha].concat(this.rgb))},g.prototype.compare=function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:void 0},g.fromKeyword=function(a){var b,c=a.toLowerCase();return f.hasOwnProperty(c)?b=new g(f[c].slice(1)):"transparent"===c&&(b=new g([0,0,0],0)),b?(b.value=a,b):void 0},b.exports=g},{"../data/colors":11,"./node":67}],48:[function(a,b){var c=a("./node"),d=function(a){" "===a?(this.value=" ",this.emptyOrWhitespace=!0):(this.value=a?a.trim():"",this.emptyOrWhitespace=""===this.value)};d.prototype=new c,d.prototype.type="Combinator";var e={"":!0," ":!0,"|":!0};d.prototype.genCSS=function(a,b){var c=a.compress||e[this.value]?"":" ";b.add(c+this.value+c)},b.exports=d},{"./node":67}],49:[function(a,b){var c=a("./node"),d=a("./debug-info"),e=function(a,b,c,d){this.value=a,this.isLineComment=b,this.currentFileInfo=d};e.prototype=new c,e.prototype.type="Comment",e.prototype.genCSS=function(a,b){this.debugInfo&&b.add(d(a,this),this.currentFileInfo,this.index),b.add(this.value)},e.prototype.isSilent=function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&"!"!==this.value[2];return this.isLineComment||b||c},e.prototype.markReferenced=function(){this.isReferenced=!0},e.prototype.isRulesetLike=function(a){return Boolean(a)},b.exports=e},{"./debug-info":51,"./node":67}],50:[function(a,b){var c=a("./node"),d=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e};d.prototype=new c,d.prototype.type="Condition",d.prototype.accept=function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},d.prototype.eval=function(a){var b=function(a,b,d){switch(a){case"and":return b&&d;case"or":return b||d;default:switch(c.compare(b,d)){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a;default:return!1}}}(this.op,this.lvalue.eval(a),this.rvalue.eval(a));return this.negate?!b:b},b.exports=d},{"./node":67}],51:[function(a,b){var c=function(a,b,d){var e="";if(a.dumpLineNumbers&&!a.compress)switch(a.dumpLineNumbers){case"comments":e=c.asComment(b);break;case"mediaquery":e=c.asMediaQuery(b);break;case"all":e=c.asComment(b)+(d||"")+c.asMediaQuery(b)}return e};c.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},c.asMediaQuery=function(a){var b=a.debugInfo.fileName;return/^[a-z]+:\/\//i.test(b)||(b="file://"+b),"@media -sass-debug-info{filename{font-family:"+b.replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},b.exports=c},{}],52:[function(a,b){var c=a("./node"),d=a("../contexts"),e=function(a,b){this.ruleset=a,this.frames=b};e.prototype=new c,e.prototype.type="DetachedRuleset",e.prototype.evalFirst=!0,e.prototype.accept=function(a){this.ruleset=a.visit(this.ruleset)},e.prototype.eval=function(a){var b=this.frames||a.frames.slice(0);return new e(this.ruleset,b)},e.prototype.callEval=function(a){return this.ruleset.eval(this.frames?new d.Eval(a,this.frames.concat(a.frames)):a)},b.exports=e},{"../contexts":10,"./node":67}],53:[function(a,b){var c=a("./node"),d=a("../data/unit-conversions"),e=a("./unit"),f=a("./color"),g=function(a,b){this.value=parseFloat(a),this.unit=b&&b instanceof e?b:new e(b?[b]:void 0)};g.prototype=new c,g.prototype.type="Dimension",g.prototype.accept=function(a){this.unit=a.visit(this.unit)},g.prototype.eval=function(){return this},g.prototype.toColor=function(){return new f([this.value,this.value,this.value])},g.prototype.genCSS=function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.fround(a,this.value),d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&this.unit.isLength())return void b.add(d);c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},g.prototype.operate=function(a,b,c){var d=this._operate(a,b,this.value,c.value),e=this.unit.clone();if("+"===b||"-"===b)if(0===e.numerator.length&&0===e.denominator.length)e.numerator=c.unit.numerator.slice(0),e.denominator=c.unit.denominator.slice(0);else if(0===c.unit.numerator.length&&0===e.denominator.length);else{if(c=c.convertTo(this.unit.usedUnits()),a.strictUnits&&c.unit.toString()!==e.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+e.toString()+"' and '"+c.unit.toString()+"'.");d=this._operate(a,b,this.value,c.value)}else"*"===b?(e.numerator=e.numerator.concat(c.unit.numerator).sort(),e.denominator=e.denominator.concat(c.unit.denominator).sort(),e.cancel()):"/"===b&&(e.numerator=e.numerator.concat(c.unit.denominator).sort(),e.denominator=e.denominator.concat(c.unit.numerator).sort(),e.cancel());return new g(d,e)},g.prototype.compare=function(a){var b,d;if(!(a instanceof g))return void 0;if(this.unit.isEmpty()||a.unit.isEmpty())b=this,d=a;else if(b=this.unify(),d=a.unify(),0!==b.unit.compare(d.unit))return void 0;return c.numericCompare(b.value,d.value)},g.prototype.unify=function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},g.prototype.convertTo=function(a){var b,c,e,f,h,i=this.value,j=this.unit.clone(),k={};if("string"==typeof a){for(b in d)d[b].hasOwnProperty(a)&&(k={},k[b]=a);a=k}h=function(a,b){return e.hasOwnProperty(a)?(b?i/=e[a]/e[f]:i*=e[a]/e[f],f):a};for(c in a)a.hasOwnProperty(c)&&(f=a[c],e=d[c],j.map(h));return j.cancel(),new g(i,j)},b.exports=g},{"../data/unit-conversions":13,"./color":47,"./node":67,"./unit":76}],54:[function(a,b){var c=a("./node"),d=a("./ruleset"),e=function(a,b,c,d,e,f,g){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f,this.isReferenced=g};e.prototype=new c,e.prototype.type="Directive",e.prototype.accept=function(a){var b=this.value,c=this.rules;c&&(this.rules=a.visit(c)),b&&(this.value=a.visit(b))},e.prototype.isRulesetLike=function(){return this.rules||!this.isCharset()},e.prototype.isCharset=function(){return"@charset"===this.name},e.prototype.genCSS=function(a,b){var c=this.value,d=this.rules;b.add(this.name,this.currentFileInfo,this.index),c&&(b.add(" "),c.genCSS(a,b)),d?("Ruleset"===d.type&&(d=[d]),this.outputRuleset(a,b,d)):b.add(";")},e.prototype.eval=function(a){var b=this.value,c=this.rules;return b&&(b=b.eval(a)),c&&(c=c.eval(a),c.root=!0),new e(this.name,b,c,this.index,this.currentFileInfo,this.debugInfo,this.isReferenced)},e.prototype.variable=function(a){return this.rules?d.prototype.variable.call(this.rules,a):void 0},e.prototype.find=function(){return this.rules?d.prototype.find.apply(this.rules,arguments):void 0},e.prototype.rulesets=function(){return this.rules?d.prototype.rulesets.apply(this.rules):void 0},e.prototype.markReferenced=function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;b.length>a;a++)b[a].markReferenced&&b[a].markReferenced()},e.prototype.getIsReferenced=function(){return!this.currentFileInfo||!this.currentFileInfo.reference||this.isReferenced},e.prototype.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--},b.exports=e},{"./node":67,"./ruleset":73}],55:[function(a,b){var c=a("./node"),d=a("./paren"),e=a("./combinator"),f=function(a,b,c,d){this.combinator=a instanceof e?a:new e(a),this.value="string"==typeof b?b.trim():b?b:"",this.index=c,this.currentFileInfo=d};f.prototype=new c,f.prototype.type="Element",f.prototype.accept=function(a){var b=this.value;this.combinator=a.visit(this.combinator),"object"==typeof b&&(this.value=a.visit(b))},f.prototype.eval=function(a){return new f(this.combinator,this.value.eval?this.value.eval(a):this.value,this.index,this.currentFileInfo)},f.prototype.genCSS=function(a,b){b.add(this.toCSS(a),this.currentFileInfo,this.index)},f.prototype.toCSS=function(a){a=a||{};var b=this.value,c=a.firstSelector;return b instanceof d&&(a.firstSelector=!0),b=b.toCSS?b.toCSS(a):b,a.firstSelector=c,""===b&&"&"===this.combinator.value.charAt(0)?"":this.combinator.toCSS(a)+b},b.exports=f},{"./combinator":48,"./node":67,"./paren":69}],56:[function(a,b){var c=a("./node"),d=a("./paren"),e=a("./comment"),f=function(a){if(this.value=a,!a)throw new Error("Expression requires an array parameter")};f.prototype=new c,f.prototype.type="Expression",f.prototype.accept=function(a){this.value=a.visitArray(this.value)},f.prototype.eval=function(a){var b,c=this.parens&&!this.parensInOp,e=!1;return c&&a.inParenthesis(),this.value.length>1?b=new f(this.value.map(function(b){return b.eval(a)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),b=this.value[0].eval(a)):b=this,c&&a.outOfParenthesis(),this.parens&&this.parensInOp&&!a.isMathOn()&&!e&&(b=new d(b)),b},f.prototype.genCSS=function(a,b){for(var c=0;this.value.length>c;c++)this.value[c].genCSS(a,b),this.value.length>c+1&&b.add(" ")},f.prototype.throwAwayComments=function(){this.value=this.value.filter(function(a){return!(a instanceof e)})},b.exports=f},{"./comment":49,"./node":67,"./paren":69}],57:[function(a,b){var c=a("./node"),d=function e(a,b,c){switch(this.selector=a,this.option=b,this.index=c,this.object_id=e.next_id++,this.parent_ids=[this.object_id],b){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}};d.next_id=0,d.prototype=new c,d.prototype.type="Extend",d.prototype.accept=function(a){this.selector=a.visit(this.selector)},d.prototype.eval=function(a){return new d(this.selector.eval(a),this.option,this.index)},d.prototype.clone=function(){return new d(this.selector,this.option,this.index)},d.prototype.findSelfSelectors=function(a){var b,c,d=[];for(b=0;a.length>b;b++)c=a[b].elements,b>0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]},b.exports=d},{"./node":67}],58:[function(a,b){var c=a("./node"),d=a("./media"),e=a("./url"),f=a("./quoted"),g=a("./ruleset"),h=a("./anonymous"),i=function(a,b,c,d,e){if(this.options=c,this.index=d,this.path=a,this.features=b,this.currentFileInfo=e,void 0!==this.options.less||this.options.inline)this.css=!this.options.less||this.options.inline;else{var f=this.getPath();f&&/[#\.\&\?\/]css([\?;].*)?$/.test(f)&&(this.css=!0)}};i.prototype=new c,i.prototype.type="Import",i.prototype.accept=function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},i.prototype.genCSS=function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},i.prototype.getPath=function(){return this.path instanceof f?this.path.value:this.path instanceof e?this.path.value.value:null},i.prototype.isVariableImport=function(){var a=this.path;return a instanceof e&&(a=a.value),a instanceof f?a.containsVariables():!0},i.prototype.evalForImport=function(a){var b=this.path;return b instanceof e&&(b=b.value),new i(b.eval(a),this.features,this.options,this.index,this.currentFileInfo)},i.prototype.evalPath=function(a){var b=this.path.eval(a),c=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(b instanceof e)){if(c){var d=b.value;d&&a.isPathRelative(d)&&(b.value=c+d)}b.value=a.normalizePath(b.value)}return b},i.prototype.eval=function(a){var b,c=this.features&&this.features.eval(a);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new h(this.root,0,{filename:this.importedFilename},!0,!0);return this.features?new d([e],this.features.value):[e]}if(this.css){var f=new i(this.evalPath(a),c,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return b=new g(null,this.root.rules.slice(0)),b.evalImports(a),this.features?new d(b.rules,this.features.value):b.rules},b.exports=i},{"./anonymous":43,"./media":63,"./node":67,"./quoted":70,"./ruleset":73,"./url":77}],59:[function(a,b){var c={};c.Node=a("./node"),c.Alpha=a("./alpha"),c.Color=a("./color"),c.Directive=a("./directive"),c.DetachedRuleset=a("./detached-ruleset"),c.Operation=a("./operation"),c.Dimension=a("./dimension"),c.Unit=a("./unit"),c.Keyword=a("./keyword"),c.Variable=a("./variable"),c.Ruleset=a("./ruleset"),c.Element=a("./element"),c.Attribute=a("./attribute"),c.Combinator=a("./combinator"),c.Selector=a("./selector"),c.Quoted=a("./quoted"),c.Expression=a("./expression"),c.Rule=a("./rule"),c.Call=a("./call"),c.URL=a("./url"),c.Import=a("./import"),c.mixin={Call:a("./mixin-call"),Definition:a("./mixin-definition")},c.Comment=a("./comment"),c.Anonymous=a("./anonymous"),c.Value=a("./value"),c.JavaScript=a("./javascript"),c.Assignment=a("./assignment"),c.Condition=a("./condition"),c.Paren=a("./paren"),c.Media=a("./media"),c.UnicodeDescriptor=a("./unicode-descriptor"),c.Negative=a("./negative"),c.Extend=a("./extend"),c.RulesetCall=a("./ruleset-call"),b.exports=c},{"./alpha":42,"./anonymous":43,"./assignment":44,"./attribute":45,"./call":46,"./color":47,"./combinator":48,"./comment":49,"./condition":50,"./detached-ruleset":52,"./dimension":53,"./directive":54,"./element":55,"./expression":56,"./extend":57,"./import":58,"./javascript":60,"./keyword":62,"./media":63,"./mixin-call":64,"./mixin-definition":65,"./negative":66,"./node":67,"./operation":68,"./paren":69,"./quoted":70,"./rule":71,"./ruleset":73,"./ruleset-call":72,"./selector":74,"./unicode-descriptor":75,"./unit":76,"./url":77,"./value":78,"./variable":79}],60:[function(a,b){var c=a("./js-eval-node"),d=a("./dimension"),e=a("./quoted"),f=a("./anonymous"),g=function(a,b,c,d){this.escaped=b,this.expression=a,this.index=c,this.currentFileInfo=d};g.prototype=new c,g.prototype.type="JavaScript",g.prototype.eval=function(a){var b=this.evaluateJavaScript(this.expression,a);return"number"==typeof b?new d(b):"string"==typeof b?new e('"'+b+'"',b,this.escaped,this.index):new f(Array.isArray(b)?b.join(", "):b)},b.exports=g},{"./anonymous":43,"./dimension":53,"./js-eval-node":61,"./quoted":70}],61:[function(a,b){var c=a("./node"),d=a("./variable"),e=function(){};e.prototype=new c,e.prototype.evaluateJavaScript=function(a,b){var c,e=this,f={};if(void 0!==b.javascriptEnabled&&!b.javascriptEnabled)throw{message:"You are using JavaScript, which has been disabled.",filename:this.currentFileInfo.filename,index:this.index};a=a.replace(/@\{([\w-]+)\}/g,function(a,c){return e.jsify(new d("@"+c,e.index,e.currentFileInfo).eval(b))});try{a=new Function("return ("+a+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+a+"`",filename:this.currentFileInfo.filename,index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(f[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=a.call(f)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",filename:this.currentFileInfo.filename,index:this.index}}return c},e.prototype.jsify=function(a){return Array.isArray(a.value)&&a.value.length>1?"["+a.value.map(function(a){return a.toCSS()}).join(", ")+"]":a.toCSS()},b.exports=e},{"./node":67,"./variable":79}],62:[function(a,b){var c=a("./node"),d=function(a){this.value=a};d.prototype=new c,d.prototype.type="Keyword",d.prototype.genCSS=function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},d.True=new d("true"),d.False=new d("false"),b.exports=d},{"./node":67}],63:[function(a,b){var c=a("./ruleset"),d=a("./value"),e=a("./element"),f=a("./selector"),g=a("./anonymous"),h=a("./expression"),i=a("./directive"),j=function(a,b,e,f){this.index=e,this.currentFileInfo=f;var g=this.emptySelectors();this.features=new d(b),this.rules=[new c(g,a)],this.rules[0].allowImports=!0};j.prototype=new i,j.prototype.type="Media",j.prototype.isRulesetLike=!0,j.prototype.accept=function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},j.prototype.genCSS=function(a,b){b.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(a,b),this.outputRuleset(a,b,this.rules)},j.prototype.eval=function(a){a.mediaBlocks||(a.mediaBlocks=[],a.mediaPath=[]);var b=new j(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,b.debugInfo=this.debugInfo);var c=!1;a.strictMath||(c=!0,a.strictMath=!0);try{b.features=this.features.eval(a)}finally{c&&(a.strictMath=!1)}return a.mediaPath.push(b),a.mediaBlocks.push(b),a.frames.unshift(this.rules[0]),b.rules=[this.rules[0].eval(a)],a.frames.shift(),a.mediaPath.pop(),0===a.mediaPath.length?b.evalTop(a):b.evalNested(a)},j.prototype.variable=function(a){return c.prototype.variable.call(this.rules[0],a)},j.prototype.find=function(){return c.prototype.find.apply(this.rules[0],arguments)},j.prototype.rulesets=function(){return c.prototype.rulesets.apply(this.rules[0])},j.prototype.emptySelectors=function(){var a=new e("","&",this.index,this.currentFileInfo),b=[new f([a],null,null,this.index,this.currentFileInfo)];return b[0].mediaEmpty=!0,b},j.prototype.markReferenced=function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;b.length>a;a++)b[a].markReferenced&&b[a].markReferenced()},j.prototype.evalTop=function(a){var b=this;if(a.mediaBlocks.length>1){var d=this.emptySelectors();b=new c(d,a.mediaBlocks),b.multiMedia=!0}return delete a.mediaBlocks,delete a.mediaPath,b},j.prototype.evalNested=function(a){var b,e,f=a.mediaPath.concat([this]);for(b=0;f.length>b;b++)e=f[b].features instanceof d?f[b].features.value:f[b].features,f[b]=Array.isArray(e)?e:[e];return this.features=new d(this.permute(f).map(function(a){for(a=a.map(function(a){return a.toCSS?a:new g(a)}),b=a.length-1;b>0;b--)a.splice(b,0,new g("and"));return new h(a)})),new c([],[])},j.prototype.permute=function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;c.length>d;d++)for(var e=0;a[0].length>e;e++)b.push([a[0][e]].concat(c[d]));return b},j.prototype.bubbleSelectors=function(a){a&&(this.rules=[new c(a.slice(0),[this.rules[0]])])},b.exports=j},{"./anonymous":43,"./directive":54,"./element":55,"./expression":56,"./ruleset":73,"./selector":74,"./value":78}],64:[function(a,b){var c=a("./node"),d=a("./selector"),e=a("./mixin-definition"),f=a("../functions/default"),g=function(a,b,c,e,f){this.selector=new d(a),this.arguments=b&&b.length?b:null,this.index=c,this.currentFileInfo=e,this.important=f};g.prototype=new c,g.prototype.type="MixinCall",g.prototype.accept=function(a){this.selector&&(this.selector=a.visit(this.selector)),this.arguments&&(this.arguments=a.visitArray(this.arguments))},g.prototype.eval=function(a){function b(b,c){var d,e;for(k=0;2>k;k++){for(w[k]=!0,f.value(k),d=0;c.length>d&&w[k];d++)e=c[d],e.matchCondition&&(w[k]=w[k]&&e.matchCondition(null,a));b.matchCondition&&(w[k]=w[k]&&b.matchCondition(h,a))}return w[0]||w[1]?w[0]!=w[1]?w[1]?z:A:y:x}var c,d,g,h,i,j,k,l,m,n,o,p,q,r,s,t=[],u=!1,v=[],w=[],x=-1,y=0,z=1,A=2;for(h=this.arguments&&this.arguments.map(function(b){return{name:b.name,value:b.value.eval(a)}}),s=function(b){return b.matchArgs(null,a)},i=0;a.frames.length>i;i++)if((c=a.frames[i].find(this.selector,null,s)).length>0){for(m=!0,j=0;c.length>j;j++){for(d=c[j].rule,g=c[j].path,l=!1,k=0;a.frames.length>k;k++)if(!(d instanceof e)&&d===(a.frames[k].originalRuleset||a.frames[k])){l=!0;break}l||d.matchArgs(h,a)&&(o={mixin:d,group:b(d,g)},o.group!==x&&v.push(o),u=!0)}for(f.reset(),q=[0,0,0],j=0;v.length>j;j++)q[v[j].group]++;if(q[y]>0)p=A;else if(p=z,q[z]+q[A]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(h)+"`",index:this.index,filename:this.currentFileInfo.filename};for(j=0;v.length>j;j++)if(o=v[j].group,o===y||o===p)try{d=v[j].mixin,d instanceof e||(r=d.originalRuleset||d,d=new e("",[],d.rules,null,!1),d.originalRuleset=r),Array.prototype.push.apply(t,d.evalCall(a,h,this.important).rules)}catch(B){throw{message:B.message,index:this.index,filename:this.currentFileInfo.filename,stack:B.stack}}if(u){if(!this.currentFileInfo||!this.currentFileInfo.reference)for(i=0;t.length>i;i++)n=t[i],n.markReferenced&&n.markReferenced();return t}}throw m?{type:"Runtime",message:"No matching definition was found for `"+this.format(h)+"`",index:this.index,filename:this.currentFileInfo.filename}:{type:"Name",message:this.selector.toCSS().trim()+" is undefined",index:this.index,filename:this.currentFileInfo.filename}},g.prototype.format=function(a){return this.selector.toCSS().trim()+"("+(a?a.map(function(a){var b="";return a.name&&(b+=a.name+":"),b+=a.value.toCSS?a.value.toCSS():"???"}).join(", "):"")+")"},b.exports=g},{"../functions/default":19,"./mixin-definition":65,"./node":67,"./selector":74}],65:[function(a,b){var c=a("./selector"),d=a("./element"),e=a("./ruleset"),f=a("./rule"),g=a("./expression"),h=a("../contexts"),i=function(a,b,e,f,g,h){this.name=a,this.selectors=[new c([new d(null,a,this.index,this.currentFileInfo)])],this.params=b,this.condition=f,this.variadic=g,this.arity=b.length,this.rules=e,this._lookups={},this.required=b.reduce(function(a,b){return!b.name||b.name&&!b.value?a+1:a},0),this.frames=h};i.prototype=new e,i.prototype.type="MixinDefinition",i.prototype.evalFirst=!0,i.prototype.accept=function(a){this.params&&this.params.length&&(this.params=a.visitArray(this.params)),this.rules=a.visitArray(this.rules),this.condition&&(this.condition=a.visit(this.condition))},i.prototype.evalParams=function(a,b,c,d){var i,j,k,l,m,n,o,p,q=new e(null,null),r=this.params.slice(0),s=0;if(b=new h.Eval(b,[q].concat(b.frames)),c)for(c=c.slice(0),s=c.length,k=0;s>k;k++)if(j=c[k],n=j&&j.name){for(o=!1,l=0;r.length>l;l++)if(!d[l]&&n===r[l].name){d[l]=j.value.eval(a),q.prependRule(new f(n,j.value.eval(a))),o=!0;break}if(o){c.splice(k,1),k--;continue}throw{type:"Runtime",message:"Named argument for "+this.name+" "+c[k].name+" not found"}}for(p=0,k=0;r.length>k;k++)if(!d[k]){if(j=c&&c[p],n=r[k].name)if(r[k].variadic){for(i=[],l=p;s>l;l++)i.push(c[l].value.eval(a));q.prependRule(new f(n,new g(i).eval(a)))}else{if(m=j&&j.value)m=m.eval(a);else{if(!r[k].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+s+" for "+this.arity+")"};m=r[k].value.eval(b),q.resetCache()}q.prependRule(new f(n,m)),d[k]=m}if(r[k].variadic&&c)for(l=p;s>l;l++)d[l]=c[l].value.eval(a);p++}return q},i.prototype.makeImportant=function(){var a=this.rules?this.rules.map(function(a){return a.makeImportant?a.makeImportant(!0):a}):this.rules,b=new i(this.name,this.params,a,this.condition,this.variadic,this.frames);return b},i.prototype.eval=function(a){return new i(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||a.frames.slice(0))},i.prototype.evalCall=function(a,b,c){var d,i,j=[],k=this.frames?this.frames.concat(a.frames):a.frames,l=this.evalParams(a,new h.Eval(a,k),b,j);return l.prependRule(new f("@arguments",new g(j).eval(a))),d=this.rules.slice(0),i=new e(null,d),i.originalRuleset=this,i=i.eval(new h.Eval(a,[this,l].concat(k))),c&&(i=i.makeImportant()),i},i.prototype.matchCondition=function(a,b){return this.condition&&!this.condition.eval(new h.Eval(b,[this.evalParams(b,new h.Eval(b,this.frames?this.frames.concat(b.frames):b.frames),a,[])].concat(this.frames).concat(b.frames)))?!1:!0},i.prototype.matchArgs=function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(this.required-1>d)return!1}else{if(this.required>d)return!1;if(d>this.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0},b.exports=i},{"../contexts":10,"./element":55,"./expression":56,"./rule":71,"./ruleset":73,"./selector":74}],66:[function(a,b){var c=a("./node"),d=a("./operation"),e=a("./dimension"),f=function(a){this.value=a};f.prototype=new c,f.prototype.type="Negative",f.prototype.genCSS=function(a,b){b.add("-"),this.value.genCSS(a,b)},f.prototype.eval=function(a){return a.isMathOn()?new d("*",[new e(-1),this.value]).eval(a):new f(this.value.eval(a))},b.exports=f},{"./dimension":53,"./node":67,"./operation":68}],67:[function(a,b){var c=function(){};c.prototype.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},c.prototype.genCSS=function(a,b){b.add(this.value)},c.prototype.accept=function(a){this.value=a.visit(this.value)},c.prototype.eval=function(){return this},c.prototype._operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}},c.prototype.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},c.compare=function(a,b){if(a.compare&&"Quoted"!==b.type&&"Anonymous"!==b.type)return a.compare(b);if(b.compare)return-b.compare(a);if(a.type!==b.type)return void 0;if(a=a.value,b=b.value,!Array.isArray(a))return a===b?0:void 0;if(a.length!==b.length)return void 0;for(var d=0;a.length>d;d++)if(0!==c.compare(a[d],b[d]))return void 0;return 0},c.numericCompare=function(a,b){return b>a?-1:a===b?0:a>b?1:void 0},b.exports=c},{}],68:[function(a,b){var c=a("./node"),d=a("./color"),e=a("./dimension"),f=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c};f.prototype=new c,f.prototype.type="Operation",f.prototype.accept=function(a){this.operands=a.visit(this.operands)},f.prototype.eval=function(a){var b=this.operands[0].eval(a),c=this.operands[1].eval(a);if(a.isMathOn()){if(b instanceof e&&c instanceof d&&(b=b.toColor()),c instanceof e&&b instanceof d&&(c=c.toColor()),!b.operate)throw{type:"Operation",message:"Operation on an invalid type"};return b.operate(a,this.op,c)}return new f(this.op,[b,c],this.isSpaced)},f.prototype.genCSS=function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},b.exports=f},{"./color":47,"./dimension":53,"./node":67}],69:[function(a,b){var c=a("./node"),d=function(a){this.value=a};d.prototype=new c,d.prototype.type="Paren",d.prototype.genCSS=function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")
+},d.prototype.eval=function(a){return new d(this.value.eval(a))},b.exports=d},{"./node":67}],70:[function(a,b){var c=a("./node"),d=a("./js-eval-node"),e=a("./variable"),f=function(a,b,c,d,e){this.escaped=null==c?!0:c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e};f.prototype=new d,f.prototype.type="Quoted",f.prototype.genCSS=function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},f.prototype.containsVariables=function(){return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/)},f.prototype.eval=function(a){function b(a,b,c){var d=a;do a=d,d=a.replace(b,c);while(a!==d);return d}var c=this,d=this.value,g=function(b,d){return String(c.evaluateJavaScript(d,a))},h=function(b,d){var g=new e("@"+d,c.index,c.currentFileInfo).eval(a,!0);return g instanceof f?g.value:g.toCSS()};return d=b(d,/`([^`]+)`/g,g),d=b(d,/@\{([\w-]+)\}/g,h),new f(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},f.prototype.compare=function(a){return"Quoted"!==a.type||this.escaped||a.escaped?a.toCSS&&this.toCSS()===a.toCSS()?0:void 0:c.numericCompare(this.value,a.value)},b.exports=f},{"./js-eval-node":61,"./node":67,"./variable":79}],71:[function(a,b){function c(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}var d=a("./node"),e=a("./value"),f=a("./keyword"),g=function(a,b,c,f,g,h,i,j){this.name=a,this.value=b instanceof d?b:new e([b]),this.important=c?" "+c.trim():"",this.merge=f,this.index=g,this.currentFileInfo=h,this.inline=i||!1,this.variable=void 0!==j?j:a.charAt&&"@"===a.charAt(0)};g.prototype=new d,g.prototype.type="Rule",g.prototype.genCSS=function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},g.prototype.eval=function(a){var b,d=!1,e=this.name,h=this.variable;"string"!=typeof e&&(e=1===e.length&&e[0]instanceof f?e[0].value:c(a,e),h=!1),"font"!==e||a.strictMath||(d=!0,a.strictMath=!0);try{if(a.importantScope.push({}),b=this.value.eval(a),!this.variable&&"DetachedRuleset"===b.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};var i=this.important,j=a.importantScope.pop();return!i&&j.important&&(i=j.important),new g(e,b,i,this.merge,this.index,this.currentFileInfo,this.inline,h)}catch(k){throw"number"!=typeof k.index&&(k.index=this.index,k.filename=this.currentFileInfo.filename),k}finally{d&&(a.strictMath=!1)}},g.prototype.makeImportant=function(){return new g(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)},b.exports=g},{"./keyword":62,"./node":67,"./value":78}],72:[function(a,b){var c=a("./node"),d=a("./variable"),e=function(a){this.variable=a};e.prototype=new c,e.prototype.type="RulesetCall",e.prototype.eval=function(a){var b=new d(this.variable).eval(a);return b.callEval(a)},b.exports=e},{"./node":67,"./variable":79}],73:[function(a,b){var c=a("./node"),d=a("./rule"),e=a("./selector"),f=a("./element"),g=a("./paren"),h=a("../contexts"),i=a("../functions/default"),j=a("./debug-info"),k=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c};k.prototype=new c,k.prototype.type="Ruleset",k.prototype.isRuleset=!0,k.prototype.isRulesetLike=!0,k.prototype.accept=function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},k.prototype.eval=function(a){var b,c,e,f,g=this.selectors,h=!1;if(g&&(c=g.length)){for(b=[],i.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;c>f;f++)e=g[f].eval(a),b.push(e),e.evaldCondition&&(h=!0);i.reset()}else h=!0;var j,l,m=this.rules?this.rules.slice(0):null,n=new k(b,m,this.strictImports);n.originalRuleset=this,n.root=this.root,n.firstRoot=this.firstRoot,n.allowImports=this.allowImports,this.debugInfo&&(n.debugInfo=this.debugInfo),h||(m.length=0);var o=a.frames;o.unshift(n);var p=a.selectors;p||(a.selectors=p=[]),p.unshift(this.selectors),(n.root||n.allowImports||!n.strictImports)&&n.evalImports(a);var q=n.rules,r=q?q.length:0;for(f=0;r>f;f++)q[f].evalFirst&&(q[f]=q[f].eval(a));var s=a.mediaBlocks&&a.mediaBlocks.length||0;for(f=0;r>f;f++)"MixinCall"===q[f].type?(m=q[f].eval(a).filter(function(a){return a instanceof d&&a.variable?!n.variable(a.name):!0}),q.splice.apply(q,[f,1].concat(m)),r+=m.length-1,f+=m.length-1,n.resetCache()):"RulesetCall"===q[f].type&&(m=q[f].eval(a).rules.filter(function(a){return a instanceof d&&a.variable?!1:!0}),q.splice.apply(q,[f,1].concat(m)),r+=m.length-1,f+=m.length-1,n.resetCache());for(f=0;q.length>f;f++)j=q[f],j.evalFirst||(q[f]=j=j.eval?j.eval(a):j);for(f=0;q.length>f;f++)if(j=q[f],j instanceof k&&j.selectors&&1===j.selectors.length&&j.selectors[0].isJustParentSelector()){q.splice(f--,1);for(var t=0;j.rules.length>t;t++)l=j.rules[t],l instanceof d&&l.variable||q.splice(++f,0,l)}if(o.shift(),p.shift(),a.mediaBlocks)for(f=s;a.mediaBlocks.length>f;f++)a.mediaBlocks[f].bubbleSelectors(b);return n},k.prototype.evalImports=function(a){var b,c,d=this.rules;if(d)for(b=0;d.length>b;b++)"Import"===d[b].type&&(c=d[b].eval(a),c&&c.length?(d.splice.apply(d,[b,1].concat(c)),b+=c.length-1):d.splice(b,1,c),this.resetCache())},k.prototype.makeImportant=function(){var a=new k(this.selectors,this.rules.map(function(a){return a.makeImportant?a.makeImportant():a}),this.strictImports);return a},k.prototype.matchArgs=function(a){return!a||0===a.length},k.prototype.matchCondition=function(a,b){var c=this.selectors[this.selectors.length-1];return c.evaldCondition?c.condition&&!c.condition.eval(new h.Eval(b,b.frames))?!1:!0:!1},k.prototype.resetCache=function(){this._rulesets=null,this._variables=null,this._lookups={}},k.prototype.variables=function(){return this._variables||(this._variables=this.rules?this.rules.reduce(function(a,b){if(b instanceof d&&b.variable===!0&&(a[b.name]=b),"Import"===b.type&&b.root&&b.root.variables){var c=b.root.variables();for(var e in c)c.hasOwnProperty(e)&&(a[e]=c[e])}return a},{}):{}),this._variables},k.prototype.variable=function(a){return this.variables()[a]},k.prototype.rulesets=function(){if(!this.rules)return null;var a,b,c=[],d=this.rules,e=d.length;for(a=0;e>a;a++)b=d[a],b.isRuleset&&c.push(b);return c},k.prototype.prependRule=function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},k.prototype.find=function(a,b,c){b=b||this;var d,f,g=[],h=a.toCSS();return h in this._lookups?this._lookups[h]:(this.rulesets().forEach(function(h){if(h!==b)for(var i=0;h.selectors.length>i;i++)if(d=a.match(h.selectors[i])){if(a.elements.length>d){if(!c||c(h)){f=h.find(new e(a.elements.slice(d)),b,c);for(var j=0;f.length>j;++j)f[j].path.push(h);Array.prototype.push.apply(g,f)}}else g.push({rule:h,path:[]});break}}),this._lookups[h]=g,g)},k.prototype.genCSS=function(a,b){function c(a,b){return"boolean"==typeof a.isRulesetLike?a.isRulesetLike:"function"==typeof a.isRulesetLike?a.isRulesetLike(b):!1}var d,e,f,g,h,i,k=[],l=[],m=[];a.tabLevel=a.tabLevel||0,this.root||a.tabLevel++;var n,o=a.compress?"":Array(a.tabLevel+1).join(" "),p=a.compress?"":Array(a.tabLevel).join(" ");for(d=0;this.rules.length>d;d++)h=this.rules[d],c(h,this.root)?m.push(h):h.isCharset&&h.isCharset()?k.push(h):l.push(h);if(l=k.concat(l),!this.root){g=j(a,this,p),g&&(b.add(g),b.add(p));var q,r=this.paths,s=r.length;for(n=a.compress?",":",\n"+p,d=0;s>d;d++)if(i=r[d],q=i.length)for(d>0&&b.add(n),a.firstSelector=!0,i[0].genCSS(a,b),a.firstSelector=!1,e=1;q>e;e++)i[e].genCSS(a,b);b.add((a.compress?"{":" {\n")+o)}for(d=0;l.length>d;d++)h=l[d],d+1!==l.length||this.root&&0!==m.length&&!this.firstRoot||(a.lastRule=!0),h.genCSS?h.genCSS(a,b):h.value&&b.add(h.value.toString()),a.lastRule?a.lastRule=!1:b.add(a.compress?"":"\n"+o);if(this.root||(b.add(a.compress?"}":"\n"+p+"}"),a.tabLevel--),n=(a.compress?"":"\n")+(this.root?o:p),f=m.length)for(l.length&&n&&b.add(n),m[0].genCSS(a,b),d=1;f>d;d++)n&&b.add(n),m[d].genCSS(a,b);b.isEmpty()||a.compress||!this.firstRoot||b.add("\n")},k.prototype.markReferenced=function(){var a;if(this.selectors)for(a=0;this.selectors.length>a;a++)this.selectors[a].markReferenced();if(this.rules)for(a=0;this.rules.length>a;a++)this.rules[a].markReferenced&&this.rules[a].markReferenced()},k.prototype.getIsReferenced=function(){var a,b,c,d;if(this.paths)for(a=0;this.paths.length>a;a++)for(c=this.paths[a],b=0;c.length>b;b++)if(c[b].getIsReferenced&&c[b].getIsReferenced())return!0;if(this.selectors)for(a=0;this.selectors.length>a;a++)if(d=this.selectors[a],d.getIsReferenced&&d.getIsReferenced())return!0;return!1},k.prototype.joinSelectors=function(a,b,c){for(var d=0;c.length>d;d++)this.joinSelector(a,b,c[d])},k.prototype.joinSelector=function(a,b,c){function d(a,b){var c,d;if(0===a.length)c=new g(a[0]);else{var h=[];for(d=0;a.length>d;d++)h.push(new f(null,a[d],b.index,b.currentFileInfo));c=new g(new e(h))}return c}function h(a,b){var c,d;return c=new f(null,a,b.index,b.currentFileInfo),d=new e([c])}function i(a,b,c){function e(a){var b;return"Paren"!==a.value.type?null:(b=a.value.value,"Selector"!==b.type?null:b)}var g,m,n,o,p,q,r,s,t,u,v=!1;for(o=[],p=[[]],g=0;c.elements.length>g;g++)if(s=c.elements[g],"&"!==s.value){var w=e(s);if(null!=w){l(o,p);var x,y=[],z=[];for(x=i(y,b,w),v=v||x,n=0;y.length>n;n++){var A=h(d(y[n],s),s);k(p,[A],s,c,z)}p=z,o=[]}else o.push(s)}else{for(v=!0,q=[],l(o,p),m=0;p.length>m;m++)if(r=p[m],0===b.length)r.length>0&&r[0].elements.push(new f(s.combinator,"",s.index,s.currentFileInfo)),q.push(r);else for(n=0;b.length>n;n++){var B=j(r,b[n],s,c);q.push(B)}p=q,o=[]}for(l(o,p),g=0;p.length>g;g++)t=p[g].length,t>0&&(a.push(p[g]),u=p[g][t-1],p[g][t-1]=u.createDerived(u.elements,c.extendList));return v}function j(a,b,c,d){var e,g,h;if(e=[],a.length>0?(e=a.slice(0),g=e.pop(),h=d.createDerived(g.elements.slice(0))):h=d.createDerived([]),b.length>0){var i=c.combinator,j=b[0].elements[0];i.emptyOrWhitespace&&!j.combinator.emptyOrWhitespace&&(i=j.combinator),h.elements.push(new f(i,j.value,c.index,c.currentFileInfo)),h.elements=h.elements.concat(b[0].elements.slice(1))}return 0!==h.elements.length&&e.push(h),b.length>1&&(e=e.concat(b.slice(1))),e}function k(a,b,c,d,e){var f;for(f=0;a.length>f;f++){var g=j(a[f],b,c,d);e.push(g)}return e}function l(a,b){var c,d;if(0!==a.length){if(0===b.length)return void b.push([new e(a)]);for(c=0;b.length>c;c++)d=b[c],d.length>0?d[d.length-1]=d[d.length-1].createDerived(d[d.length-1].elements.concat(a)):d.push(new e(a))}}var m,n,o;if(n=[],o=i(n,b,c),!o)if(b.length>0)for(n=[],m=0;b.length>m;m++)n.push(b[m].concat(c));else n=[[c]];for(m=0;n.length>m;m++)a.push(n[m])},b.exports=k},{"../contexts":10,"../functions/default":19,"./debug-info":51,"./element":55,"./node":67,"./paren":69,"./rule":71,"./selector":74}],74:[function(a,b){var c=a("./node"),d=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)};d.prototype=new c,d.prototype.type="Selector",d.prototype.accept=function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},d.prototype.createDerived=function(a,b,c){c=null!=c?c:this.evaldCondition;var e=new d(a,b||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=c,e.mediaEmpty=this.mediaEmpty,e},d.prototype.match=function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},d.prototype.CacheElements=function(){if(!this._elements){var a=this.elements.map(function(a){return a.combinator.value+(a.value.value||a.value)}).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);a?"&"===a[0]&&a.shift():a=[],this._elements=a}},d.prototype.isJustParentSelector=function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},d.prototype.eval=function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},d.prototype.genCSS=function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;this.elements.length>c;c++)d=this.elements[c],d.genCSS(a,b)},d.prototype.markReferenced=function(){this.isReferenced=!0},d.prototype.getIsReferenced=function(){return!this.currentFileInfo.reference||this.isReferenced},d.prototype.getIsOutput=function(){return this.evaldCondition},b.exports=d},{"./node":67}],75:[function(a,b){var c=a("./node"),d=function(a){this.value=a};d.prototype=new c,d.prototype.type="UnicodeDescriptor",b.exports=d},{"./node":67}],76:[function(a,b){var c=a("./node"),d=a("../data/unit-conversions"),e=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],c?this.backupUnit=c:a&&a.length&&(this.backupUnit=a[0])};e.prototype=new c,e.prototype.type="Unit",e.prototype.clone=function(){return new e(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},e.prototype.genCSS=function(a,b){var c=a&&a.strictUnits;1===this.numerator.length?b.add(this.numerator[0]):!c&&this.backupUnit?b.add(this.backupUnit):!c&&this.denominator.length&&b.add(this.denominator[0])},e.prototype.toString=function(){var a,b=this.numerator.join("*");for(a=0;this.denominator.length>a;a++)b+="/"+this.denominator[a];return b},e.prototype.compare=function(a){return this.is(a.toString())?0:void 0},e.prototype.is=function(a){return this.toString().toUpperCase()===a.toUpperCase()},e.prototype.isLength=function(){return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/))},e.prototype.isEmpty=function(){return 0===this.numerator.length&&0===this.denominator.length},e.prototype.isSingular=function(){return 1>=this.numerator.length&&0===this.denominator.length},e.prototype.map=function(a){var b;for(b=0;this.numerator.length>b;b++)this.numerator[b]=a(this.numerator[b],!1);for(b=0;this.denominator.length>b;b++)this.denominator[b]=a(this.denominator[b],!0)},e.prototype.usedUnits=function(){var a,b,c={};b=function(b){return a.hasOwnProperty(b)&&!c[e]&&(c[e]=b),b};for(var e in d)d.hasOwnProperty(e)&&(a=d[e],this.map(b));return c},e.prototype.cancel=function(){var a,b,c={};for(b=0;this.numerator.length>b;b++)a=this.numerator[b],c[a]=(c[a]||0)+1;for(b=0;this.denominator.length>b;b++)a=this.denominator[b],c[a]=(c[a]||0)-1;this.numerator=[],this.denominator=[];for(a in c)if(c.hasOwnProperty(a)){var d=c[a];if(d>0)for(b=0;d>b;b++)this.numerator.push(a);else if(0>d)for(b=0;-d>b;b++)this.denominator.push(a)}this.numerator.sort(),this.denominator.sort()},b.exports=e},{"../data/unit-conversions":13,"./node":67}],77:[function(a,b){var c=a("./node"),d=function(a,b,c,d){this.value=a,this.currentFileInfo=c,this.index=b,this.isEvald=d};d.prototype=new c,d.prototype.type="Url",d.prototype.accept=function(a){this.value=a.visit(this.value)},d.prototype.genCSS=function(a,b){b.add("url("),this.value.genCSS(a,b),b.add(")")},d.prototype.eval=function(a){var b,c=this.value.eval(a);if(!this.isEvald&&(b=this.currentFileInfo&&this.currentFileInfo.rootpath,b&&"string"==typeof c.value&&a.isPathRelative(c.value)&&(c.quote||(b=b.replace(/[\(\)'"\s]/g,function(a){return"\\"+a})),c.value=b+c.value),c.value=a.normalizePath(c.value),a.urlArgs&&!c.value.match(/^\s*data:/))){var e=-1===c.value.indexOf("?")?"?":"&",f=e+a.urlArgs;-1!==c.value.indexOf("#")?c.value=c.value.replace("#",f+"#"):c.value+=f}return new d(c,this.index,this.currentFileInfo,!0)},b.exports=d},{"./node":67}],78:[function(a,b){var c=a("./node"),d=function(a){if(this.value=a,!a)throw new Error("Value requires an array argument")};d.prototype=new c,d.prototype.type="Value",d.prototype.accept=function(a){this.value&&(this.value=a.visitArray(this.value))},d.prototype.eval=function(a){return 1===this.value.length?this.value[0].eval(a):new d(this.value.map(function(b){return b.eval(a)}))},d.prototype.genCSS=function(a,b){var c;for(c=0;this.value.length>c;c++)this.value[c].genCSS(a,b),this.value.length>c+1&&b.add(a&&a.compress?",":", ")},b.exports=d},{"./node":67}],79:[function(a,b){var c=a("./node"),d=function(a,b,c){this.name=a,this.index=b,this.currentFileInfo=c||{}};d.prototype=new c,d.prototype.type="Variable",d.prototype.eval=function(a){var b,c=this.name;if(0===c.indexOf("@@")&&(c="@"+new d(c.slice(1),this.index,this.currentFileInfo).eval(a).value),this.evaluating)throw{type:"Name",message:"Recursive variable definition for "+c,filename:this.currentFileInfo.filename,index:this.index};if(this.evaluating=!0,b=this.find(a.frames,function(b){var d=b.variable(c);if(d){if(d.important){var e=a.importantScope[a.importantScope.length-1];e.important=d.important}return d.value.eval(a)}}))return this.evaluating=!1,b;throw{type:"Name",message:"variable "+c+" is undefined",filename:this.currentFileInfo.filename,index:this.index}},d.prototype.find=function(a,b){for(var c,d=0;a.length>d;d++)if(c=b.call(a,a[d]))return c;return null},b.exports=d},{"./node":67}],80:[function(a,b){b.exports={getLocation:function(a,b){for(var c=a+1,d=null,e=-1;--c>=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}}},{}],81:[function(a,b){var c=a("../tree"),d=a("./visitor"),e=a("../logger"),f=function(){this._visitor=new d(this),this.contexts=[],this.allExtendsStack=[[]]};f.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,d,e,f,g=[],h=a.rules,i=h?h.length:0;for(b=0;i>b;b++)a.rules[b]instanceof c.Extend&&(g.push(h[b]),a.extendOnEveryPath=!0);var j=a.paths;for(b=0;j.length>b;b++){var k=j[b],l=k[k.length-1],m=l.extendList;for(f=m?m.slice(0).concat(g):g,f&&(f=f.map(function(a){return a.clone()})),d=0;f.length>d;d++)this.foundExtends=!0,e=f[d],e.findSelfSelectors(k),e.ruleset=a,0===d&&(e.firstExtendOnThisSelectorPath=!0),this.allExtendsStack[this.allExtendsStack.length-1].push(e)}this.contexts.push(a.selectors)}},visitRulesetOut:function(a){a.root||(this.contexts.length=this.contexts.length-1)},visitMedia:function(a){a.allExtends=[],this.allExtendsStack.push(a.allExtends)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){a.allExtends=[],this.allExtendsStack.push(a.allExtends)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}};var g=function(){this._visitor=new d(this)};g.prototype={run:function(a){var b=new f;if(this.extendIndicies={},b.run(a),!b.foundExtends)return a;a.allExtends=a.allExtends.concat(this.doExtendChaining(a.allExtends,a.allExtends)),this.allExtendsStack=[a.allExtends];var c=this._visitor.visit(a);return this.checkExtendsForNonMatched(a.allExtends),c},checkExtendsForNonMatched:function(a){var b=this.extendIndicies;a.filter(function(a){return!a.hasFoundMatches&&1==a.parent_ids.length}).forEach(function(a){var c="_unknown_";try{c=a.selector.toCSS({})}catch(d){}b[a.index+" "+c]||(b[a.index+" "+c]=!0,e.warn("extend '"+c+"' has no matches"))})},doExtendChaining:function(a,b,d){var e,f,g,h,i,j,k,l,m=[],n=this;for(d=d||0,e=0;a.length>e;e++)for(f=0;b.length>f;f++)j=a[e],k=b[f],j.parent_ids.indexOf(k.object_id)>=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&(j.hasFoundMatches=!0,j.selfSelectors.forEach(function(a){h=n.extendSelector(g,i,a),l=new c.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))})));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,b,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;f.length>d;d++)for(c=0;a.paths.length>c;c++)if(e=a.paths[c],!a.extendOnEveryPath){var i=e[e.length-1].extendList;i&&i.length||(b=this.findMatch(f[d],e),b.length&&(f[d].hasFoundMatches=!0,f[d].selfSelectors.forEach(function(a){g.push(h.extendSelector(b,e,a))})))}a.paths=a.paths.concat(g)}},findMatch:function(a,b){var c,d,e,f,g,h,i,j=this,k=a.selector.elements,l=[],m=[];for(c=0;b.length>c;c++)for(d=b[c],e=0;d.elements.length>e;e++)for(f=d.elements[e],(a.allowBefore||0===c&&0===e)&&l.push({pathIndex:c,index:e,matched:0,initialCombinator:f.combinator}),h=0;l.length>h;h++)i=l[h],g=f.combinator.value,""===g&&0===e&&(g=" "),!j.isElementValuesEqual(k[i.matched].value,f.value)||i.matched>0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(d.elements.length>e+1||b.length>c+1)&&(i=null)),i?i.finished&&(i.length=k.length,i.endPathIndex=c,i.endPathElementIndex=e+1,l.length=0,m.push(i)):(l.splice(h,1),h--);return m},isElementValuesEqual:function(a,b){if("string"==typeof a||"string"==typeof b)return a===b;if(a instanceof c.Attribute)return a.op!==b.op||a.key!==b.key?!1:a.value&&b.value?(a=a.value.value||a.value,b=b.value.value||b.value,a===b):a.value||b.value?!1:!0;if(a=a.value,b=b.value,a instanceof c.Selector){if(!(b instanceof c.Selector)||a.elements.length!==b.elements.length)return!1;for(var d=0;a.elements.length>d;d++){if(a.elements[d].combinator.value!==b.elements[d].combinator.value&&(0!==d||(a.elements[d].combinator.value||" ")!==(b.elements[d].combinator.value||" ")))return!1;if(!this.isElementValuesEqual(a.elements[d].value,b.elements[d].value))return!1}return!0}return!1},extendSelector:function(a,b,d){var e,f,g,h,i,j=0,k=0,l=[];for(e=0;a.length>e;e++)h=a[e],f=b[h.pathIndex],g=new c.Element(h.initialCombinator,d.elements[0].value,d.elements[0].index,d.elements[0].currentFileInfo),h.pathIndex>j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(b[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(b.slice(j,h.pathIndex)),l.push(new c.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=b[j].elements.length&&(k=0,j++);return b.length>j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(b[j].elements.slice(k)),j++),l=l.concat(b.slice(j,b.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){var a=this.allExtendsStack.length-1;this.checkExtendsForNonMatched(this.allExtendsStack[a]),this.allExtendsStack.length=a},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){var a=this.allExtendsStack.length-1;this.checkExtendsForNonMatched(this.allExtendsStack[a]),this.allExtendsStack.length=a}},b.exports=g},{"../logger":31,"../tree":59,"./visitor":87}],82:[function(a,b){function c(a){this.imports=[],this.variableImports=[],this._onSequencerEmpty=a,this._currentDepth=0}c.prototype.addImport=function(a){var b=this,c={callback:a,args:null,isReady:!1};return this.imports.push(c),function(){c.args=Array.prototype.slice.call(arguments,0),c.isReady=!0,b.tryRun()}},c.prototype.addVariableImport=function(a){this.variableImports.push(a)},c.prototype.tryRun=function(){this._currentDepth++;try{for(;;){for(;this.imports.length>0;){var a=this.imports[0];if(!a.isReady)return;this.imports=this.imports.slice(1),a.callback.apply(null,a.args)}if(0===this.variableImports.length)break;var b=this.variableImports[0];this.variableImports=this.variableImports.slice(1),b()}}finally{this._currentDepth--}0===this._currentDepth&&this._onSequencerEmpty&&this._onSequencerEmpty()},b.exports=c},{}],83:[function(a,b){var c=a("../contexts"),d=a("./visitor"),e=a("./import-sequencer"),f=function(a,b){this._visitor=new d(this),this._importer=a,this._finish=b,this.context=new c.Eval,this.importCount=0,this.onceFileDetectionMap={},this.recursionDetector={},this._sequencer=new e(this._onSequencerEmpty.bind(this))};f.prototype={isReplacing:!1,run:function(a){try{this._visitor.visit(a)}catch(b){this.error=b}this.isFinished=!0,this._sequencer.tryRun()},_onSequencerEmpty:function(){this.isFinished&&this._finish(this.error)},visitImport:function(a,b){var d=a.options.inline;if(!a.css||d){var e=new c.Eval(this.context,this.context.frames.slice(0)),f=e.frames[0];this.importCount++,a.isVariableImport()?this._sequencer.addVariableImport(this.processImportNode.bind(this,a,e,f)):this.processImportNode(a,e,f)}b.visitDeeper=!1},processImportNode:function(a,b,c){var d,e=a.options.inline;try{d=a.evalForImport(b)}catch(f){f.filename||(f.index=a.index,f.filename=a.currentFileInfo.filename),a.css=!0,a.error=f}if(!d||d.css&&!e)this.importCount--,this.isFinished&&this._sequencer.tryRun();else{d.options.multiple&&(b.importMultiple=!0);for(var g=void 0===d.css,h=0;c.rules.length>h;h++)if(c.rules[h]===a){c.rules[h]=d;break}var i=this.onImported.bind(this,d,b),j=this._sequencer.addImport(i);this._importer.push(d.getPath(),g,d.currentFileInfo,d.options,j)}},onImported:function(a,b,c,d,e,f){c&&(c.filename||(c.index=a.index,c.filename=a.currentFileInfo.filename),this.error=c);var g=this,h=a.options.inline,i=e||f in g.recursionDetector;if(b.importMultiple||(a.skip=i?!0:function(){return f in g.onceFileDetectionMap?!0:(g.onceFileDetectionMap[f]=!0,!1)}),d&&(a.root=d,a.importedFilename=f,!h&&(b.importMultiple||!i))){g.recursionDetector[f]=!0;var j=this.context;this.context=b;try{this._visitor.visit(d)}catch(c){this.error=c}this.context=j}g.importCount--,g.isFinished&&g._sequencer.tryRun()},visitRule:function(a,b){b.visitDeeper=!1},visitDirective:function(a){this.context.frames.unshift(a)},visitDirectiveOut:function(){this.context.frames.shift()},visitMixinDefinition:function(a){this.context.frames.unshift(a)},visitMixinDefinitionOut:function(){this.context.frames.shift()},visitRuleset:function(a){this.context.frames.unshift(a)},visitRulesetOut:function(){this.context.frames.shift()},visitMedia:function(a){this.context.frames.unshift(a.rules[0])},visitMediaOut:function(){this.context.frames.shift()}},b.exports=f},{"../contexts":10,"./import-sequencer":82,"./visitor":87}],84:[function(a,b){var c={Visitor:a("./visitor"),ImportVisitor:a("./import-visitor"),ExtendVisitor:a("./extend-visitor"),JoinSelectorVisitor:a("./join-selector-visitor"),ToCSSVisitor:a("./to-css-visitor")};b.exports=c},{"./extend-visitor":81,"./import-visitor":83,"./join-selector-visitor":85,"./to-css-visitor":86,"./visitor":87}],85:[function(a,b){var c=a("./visitor"),d=function(){this.contexts=[[]],this._visitor=new c(this)};d.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}},b.exports=d},{"./visitor":87}],86:[function(a,b){var c=a("../tree"),d=a("./visitor"),e=function(a){this._visitor=new d(this),this._context=a};e.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?void 0:a},visitMixinDefinition:function(a){a.frames=[]},visitExtend:function(){},visitComment:function(a){return a.isSilent(this._context)?void 0:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:void 0},visitDirective:function(a,b){if("@charset"===a.name){if(!a.getIsReferenced())return;if(this.charset){if(a.debugInfo){var d=new c.Comment("/* "+a.toCSS(this._context).replace(/\n/g,"")+" */\n");return d.debugInfo=a.debugInfo,this._visitor.visit(d)}return}this.charset=!0}if(a.rules&&a.rules.rules){if(this._mergeRules(a.rules.rules),a.accept(this._visitor),b.visitDeeper=!1,a.getIsReferenced())return a;if(!a.rules.rules)return;for(var e=0;a.rules.rules.length>e;e++){var f=a.rules.rules[e];if(f.getIsReferenced&&f.getIsReferenced())return a.markReferenced(),a}}else if(a.getIsReferenced())return a},checkPropertiesInRoot:function(a){for(var b,d=0;a.length>d;d++)if(b=a[d],b instanceof c.Rule&&!b.variable)throw{message:"properties must be inside selector blocks, they cannot be in the root.",index:b.index,filename:b.currentFileInfo?b.currentFileInfo.filename:null}},visitRuleset:function(a,b){var d,e=[];if(a.firstRoot&&this.checkPropertiesInRoot(a.rules),a.root)a.accept(this._visitor),b.visitDeeper=!1,(a.firstRoot||a.rules&&a.rules.length>0)&&e.splice(0,0,a);else{a.paths&&(a.paths=a.paths.filter(function(a){var b;for(" "===a[0].elements[0].combinator.value&&(a[0].elements[0].combinator=new c.Combinator("")),b=0;a.length>b;b++)if(a[b].getIsReferenced()&&a[b].getIsOutput())return!0;return!1}));for(var f=a.rules,g=f?f.length:0,h=0;g>h;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?a.accept(this._visitor):a.rules=null,b.visitDeeper=!1,f=a.rules,f&&(this._mergeRules(f),f=a.rules),f&&(this._removeDuplicateRules(f),f=a.rules),f&&f.length>0&&a.paths.length>0&&e.splice(0,0,a)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(a){if(a){var b,d,e,f={};for(e=a.length-1;e>=0;e--)if(d=a[e],d instanceof c.Rule)if(f[d.name]){b=f[d.name],b instanceof c.Rule&&(b=f[d.name]=[f[d.name].toCSS(this._context)]);var g=d.toCSS(this._context);-1!==b.indexOf(g)?a.splice(e,1):b.push(g)}else f[d.name]=d}},_mergeRules:function(a){if(a){for(var b,d,e,f={},g=0;a.length>g;g++)d=a[g],d instanceof c.Rule&&d.merge&&(e=[d.name,d.important?"!":""].join(","),f[e]?a.splice(g--,1):f[e]=[],f[e].push(d));Object.keys(f).map(function(a){function e(a){return new c.Expression(a.map(function(a){return a.value}))}function g(a){return new c.Value(a.map(function(a){return a}))}if(b=f[a],b.length>1){d=b[0];var h=[],i=[];b.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}},b.exports=e},{"../tree":59,"./visitor":87}],87:[function(a,b){function c(a){return a}function d(a,b){var c,e;for(c in a)if(a.hasOwnProperty(c))switch(e=a[c],typeof e){case"function":e.prototype&&e.prototype.type&&(e.prototype.typeIndex=b++);break;case"object":b=d(e,b)}return b}var e=a("../tree"),f={visitDeeper:!0},g=!1,h=function(a){this._implementation=a,this._visitFnCache=[],g||(d(e,1),g=!0)};h.prototype={visit:function(a){if(!a)return a;var b=a.typeIndex;if(!b)return a;var d,e=this._visitFnCache,g=this._implementation,h=b<<1,i=1|h,j=e[h],k=e[i],l=f;if(l.visitDeeper=!0,j||(d="visit"+a.type,j=g[d]||c,k=g[d+"Out"]||c,e[h]=j,e[i]=k),j!==c){var m=j.call(g,a,l);
+g.isReplacing&&(a=m)}return l.visitDeeper&&a&&a.accept&&a.accept(this),k!=c&&k.call(g,a),a},visitArray:function(a,b){if(!a)return a;var c,d=a.length;if(b||!this._implementation.isReplacing){for(c=0;d>c;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);void 0!==f&&(f.splice?f.length&&this.flatten(f,e):e.push(f))}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],void 0!==e)if(e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],void 0!==h&&(h.splice?h.length&&this.flatten(h,b):b.push(h));else b.push(e);return b}},b.exports=h},{"../tree":59}],88:[function(a,b){function c(){if(!g){g=!0;for(var a,b=f.length;b;){a=f,f=[];for(var c=-1;++c<b;)a[c]();b=f.length}g=!1}}function d(){}var e=b.exports={},f=[],g=!1;e.nextTick=function(a){f.push(a),g||setTimeout(c,0)},e.title="browser",e.browser=!0,e.env={},e.argv=[],e.version="",e.on=d,e.addListener=d,e.once=d,e.off=d,e.removeListener=d,e.removeAllListeners=d,e.emit=d,e.binding=function(){throw new Error("process.binding is not supported")},e.cwd=function(){return"/"},e.chdir=function(){throw new Error("process.chdir is not supported")},e.umask=function(){return 0}},{}],89:[function(a,b){"use strict";function c(a){function b(a){return null===i?void k.push(a):void f(function(){var b=i?a.onFulfilled:a.onRejected;if(null===b)return void(i?a.resolve:a.reject)(j);var c;try{c=b(j)}catch(d){return void a.reject(d)}a.resolve(c)})}function c(a){try{if(a===l)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var b=a.then;if("function"==typeof b)return void e(b.bind(a),c,g)}i=!0,j=a,h()}catch(d){g(d)}}function g(a){i=!1,j=a,h()}function h(){for(var a=0,c=k.length;c>a;a++)b(k[a]);k=null}if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");var i=null,j=null,k=[],l=this;this.then=function(a,c){return new l.constructor(function(e,f){b(new d(a,c,e,f))})},e(a,c,g)}function d(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function e(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var f=a("asap");b.exports=c},{asap:91}],90:[function(a,b){"use strict";function c(a){this.then=function(b){return"function"!=typeof b?this:new d(function(c,d){e(function(){try{c(b(a))}catch(e){d(e)}})})}}var d=a("./core.js"),e=a("asap");b.exports=d,c.prototype=d.prototype;var f=new c(!0),g=new c(!1),h=new c(null),i=new c(void 0),j=new c(0),k=new c("");d.resolve=function(a){if(a instanceof d)return a;if(null===a)return h;if(void 0===a)return i;if(a===!0)return f;if(a===!1)return g;if(0===a)return j;if(""===a)return k;if("object"==typeof a||"function"==typeof a)try{var b=a.then;if("function"==typeof b)return new d(b.bind(a))}catch(e){return new d(function(a,b){b(e)})}return new c(a)},d.all=function(a){var b=Array.prototype.slice.call(a);return new d(function(a,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}b[f]=g,0===--e&&a(b)}catch(i){c(i)}}if(0===b.length)return a([]);for(var e=b.length,f=0;b.length>f;f++)d(f,b[f])})},d.reject=function(a){return new d(function(b,c){c(a)})},d.race=function(a){return new d(function(b,c){a.forEach(function(a){d.resolve(a).then(b,c)})})},d.prototype["catch"]=function(a){return this.then(null,a)}},{"./core.js":89,asap:91}],91:[function(a,b){(function(a){function c(){for(;e.next;){e=e.next;var a=e.task;e.task=void 0;var b=e.domain;b&&(e.domain=void 0,b.enter());try{a()}catch(d){if(i)throw b&&b.exit(),setTimeout(c,0),b&&b.enter(),d;setTimeout(function(){throw d},0)}b&&b.exit()}g=!1}function d(b){f=f.next={task:b,domain:i&&a.domain,next:null},g||(g=!0,h())}var e={task:void 0,next:null},f=e,g=!1,h=void 0,i=!1;if("undefined"!=typeof a&&a.nextTick)i=!0,h=function(){a.nextTick(c)};else if("function"==typeof setImmediate)h="undefined"!=typeof window?setImmediate.bind(window,c):function(){setImmediate(c)};else if("undefined"!=typeof MessageChannel){var j=new MessageChannel;j.port1.onmessage=c,h=function(){j.port2.postMessage(0)}}else h=function(){setTimeout(c,0)};b.exports=d}).call(this,a("_process"))},{_process:88}],92:[function(){"function"!=typeof Promise.prototype.done&&(Promise.prototype.done=function(){var a=arguments.length?this.then.apply(this,arguments):this;a.then(null,function(a){setTimeout(function(){throw a},0)})})},{}],"promise/polyfill.js":[function(a){a("asap");"undefined"==typeof Promise&&(Promise=a("./lib/core.js"),a("./lib/es6-extensions.js")),a("./polyfill-done.js")},{"./lib/core.js":89,"./lib/es6-extensions.js":90,"./polyfill-done.js":92,asap:91}]},{},[2])(2)});
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
--- /dev/null
+@if "%DEBUG%" == "" @echo off\r
+@rem ##########################################################################\r
+@rem\r
+@rem Gradle startup script for Windows\r
+@rem\r
+@rem ##########################################################################\r
+\r
+@rem Set local scope for the variables with windows NT shell\r
+if "%OS%"=="Windows_NT" setlocal\r
+\r
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r
+set DEFAULT_JVM_OPTS=\r
+\r
+set DIRNAME=%~dp0\r
+if "%DIRNAME%" == "" set DIRNAME=.\r
+set APP_BASE_NAME=%~n0\r
+set APP_HOME=%DIRNAME%\r
+\r
+@rem Find java.exe\r
+if defined JAVA_HOME goto findJavaFromJavaHome\r
+\r
+set JAVA_EXE=java.exe\r
+%JAVA_EXE% -version >NUL 2>&1\r
+if "%ERRORLEVEL%" == "0" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:findJavaFromJavaHome\r
+set JAVA_HOME=%JAVA_HOME:"=%\r
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe\r
+\r
+if exist "%JAVA_EXE%" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:init\r
+@rem Get command-line arguments, handling Windowz variants\r
+\r
+if not "%OS%" == "Windows_NT" goto win9xME_args\r
+if "%@eval[2+2]" == "4" goto 4NT_args\r
+\r
+:win9xME_args\r
+@rem Slurp the command line arguments.\r
+set CMD_LINE_ARGS=\r
+set _SKIP=2\r
+\r
+:win9xME_args_slurp\r
+if "x%~1" == "x" goto execute\r
+\r
+set CMD_LINE_ARGS=%*\r
+goto execute\r
+\r
+:4NT_args\r
+@rem Get arguments from the 4NT Shell from JP Software\r
+set CMD_LINE_ARGS=%$\r
+\r
+:execute\r
+@rem Setup the command line\r
+\r
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar\r
+\r
+@rem Execute Gradle\r
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r
+\r
+:end\r
+@rem End local scope for the variables with windows NT shell\r
+if "%ERRORLEVEL%"=="0" goto mainEnd\r
+\r
+:fail\r
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r
+rem the _cmd.exe /c_ return code!\r
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1\r
+exit /b 1\r
+\r
+:mainEnd\r
+if "%OS%"=="Windows_NT" endlocal\r
+\r
+:omega\r
--- /dev/null
+module.exports = require('./lib/less-node');
--- /dev/null
+var addDataAttr = require("./utils").addDataAttr,
+ browser = require("./browser");
+
+module.exports = function(window, options) {
+
+ // use options from the current script tag data attribues
+ addDataAttr(options, browser.currentScript(window));
+
+ if (options.isFileProtocol === undefined) {
+ options.isFileProtocol = /^(file|(chrome|safari)(-extension)?|resource|qrc|app):/.test(window.location.protocol);
+ }
+
+ // Load styles asynchronously (default: false)
+ //
+ // This is set to `false` by default, so that the body
+ // doesn't start loading before the stylesheets are parsed.
+ // Setting this to `true` can result in flickering.
+ //
+ options.async = options.async || false;
+ options.fileAsync = options.fileAsync || false;
+
+ // Interval between watch polls
+ options.poll = options.poll || (options.isFileProtocol ? 1000 : 1500);
+
+ options.env = options.env || (window.location.hostname == '127.0.0.1' ||
+ window.location.hostname == '0.0.0.0' ||
+ window.location.hostname == 'localhost' ||
+ (window.location.port &&
+ window.location.port.length > 0) ||
+ options.isFileProtocol ? 'development'
+ : 'production');
+
+ var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(window.location.hash);
+ if (dumpLineNumbers) {
+ options.dumpLineNumbers = dumpLineNumbers[1];
+ }
+
+ if (options.useFileCache === undefined) {
+ options.useFileCache = true;
+ }
+
+ if (options.onReady === undefined) {
+ options.onReady = true;
+ }
+
+};
--- /dev/null
+/**
+ * Kicks off less and compiles any stylesheets
+ * used in the browser distributed version of less
+ * to kick-start less using the browser api
+ */
+/*global window */
+
+// shim Promise if required
+require('promise/polyfill.js');
+
+var options = window.less || {};
+require("./add-default-options")(window, options);
+
+var less = module.exports = require("./index")(window, options);
+
+if (options.onReady) {
+ if (/!watch/.test(window.location.hash)) {
+ less.watch();
+ }
+
+ less.pageLoadFinished = less.registerStylesheets().then(
+ function () {
+ return less.refresh(less.env === 'development');
+ }
+ );
+}
\ No newline at end of file
--- /dev/null
+var utils = require("./utils");
+module.exports = {
+ createCSS: function (document, styles, sheet) {
+ // Strip the query-string
+ var href = sheet.href || '';
+
+ // If there is no title set, use the filename, minus the extension
+ var id = 'less:' + (sheet.title || utils.extractId(href));
+
+ // If this has already been inserted into the DOM, we may need to replace it
+ var oldStyleNode = document.getElementById(id);
+ var keepOldStyleNode = false;
+
+ // Create a new stylesheet node for insertion or (if necessary) replacement
+ var styleNode = document.createElement('style');
+ styleNode.setAttribute('type', 'text/css');
+ if (sheet.media) {
+ styleNode.setAttribute('media', sheet.media);
+ }
+ styleNode.id = id;
+
+ if (!styleNode.styleSheet) {
+ styleNode.appendChild(document.createTextNode(styles));
+
+ // If new contents match contents of oldStyleNode, don't replace oldStyleNode
+ keepOldStyleNode = (oldStyleNode !== null && oldStyleNode.childNodes.length > 0 && styleNode.childNodes.length > 0 &&
+ oldStyleNode.firstChild.nodeValue === styleNode.firstChild.nodeValue);
+ }
+
+ var head = document.getElementsByTagName('head')[0];
+
+ // If there is no oldStyleNode, just append; otherwise, only append if we need
+ // to replace oldStyleNode with an updated stylesheet
+ if (oldStyleNode === null || keepOldStyleNode === false) {
+ var nextEl = sheet && sheet.nextSibling || null;
+ if (nextEl) {
+ nextEl.parentNode.insertBefore(styleNode, nextEl);
+ } else {
+ head.appendChild(styleNode);
+ }
+ }
+ if (oldStyleNode && keepOldStyleNode === false) {
+ oldStyleNode.parentNode.removeChild(oldStyleNode);
+ }
+
+ // For IE.
+ // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash.
+ // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head
+ if (styleNode.styleSheet) {
+ try {
+ styleNode.styleSheet.cssText = styles;
+ } catch (e) {
+ throw new Error("Couldn't reassign styleSheet.cssText.");
+ }
+ }
+ },
+ currentScript: function(window) {
+ var document = window.document;
+ return document.currentScript || (function() {
+ var scripts = document.getElementsByTagName("script");
+ return scripts[scripts.length - 1];
+ })();
+ }
+};
--- /dev/null
+// Cache system is a bit outdated and could do with work
+
+module.exports = function(window, options, logger) {
+ var cache = null;
+ if (options.env !== 'development') {
+ try {
+ cache = (typeof window.localStorage === 'undefined') ? null : window.localStorage;
+ } catch (_) {}
+ }
+ return {
+ setCSS: function(path, lastModified, styles) {
+ if (cache) {
+ logger.info('saving ' + path + ' to cache.');
+ try {
+ cache.setItem(path, styles);
+ cache.setItem(path + ':timestamp', lastModified);
+ } catch(e) {
+ //TODO - could do with adding more robust error handling
+ logger.error('failed to save "' + path + '" to local storage for caching.');
+ }
+ }
+ },
+ getCSS: function(path, webInfo) {
+ var css = cache && cache.getItem(path),
+ timestamp = cache && cache.getItem(path + ':timestamp');
+
+ if (timestamp && webInfo.lastModified &&
+ (new Date(webInfo.lastModified).valueOf() ===
+ new Date(timestamp).valueOf())) {
+ // Use local copy
+ return css;
+ }
+ }
+ };
+};
--- /dev/null
+var utils = require("./utils"),
+ browser = require("./browser");
+
+module.exports = function(window, less, options) {
+
+ function errorHTML(e, rootHref) {
+ var id = 'less-error-message:' + utils.extractId(rootHref || "");
+ var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
+ var elem = window.document.createElement('div'), timer, content, errors = [];
+ var filename = e.filename || rootHref;
+ var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1];
+
+ elem.id = id;
+ elem.className = "less-error-message";
+
+ content = '<h3>' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
+ '</h3>' + '<p>in <a href="' + filename + '">' + filenameNoPath + "</a> ";
+
+ var errorline = function (e, i, classname) {
+ if (e.extract[i] !== undefined) {
+ errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
+ .replace(/\{class\}/, classname)
+ .replace(/\{content\}/, e.extract[i]));
+ }
+ };
+
+ if (e.extract) {
+ errorline(e, 0, '');
+ errorline(e, 1, 'line');
+ errorline(e, 2, '');
+ content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
+ '<ul>' + errors.join('') + '</ul>';
+ }
+ if (e.stack && (e.extract || options.logLevel >= 4)) {
+ content += '<br/>Stack Trace</br />' + e.stack.split('\n').slice(1).join('<br/>');
+ }
+ elem.innerHTML = content;
+
+ // CSS for error messages
+ browser.createCSS(window.document, [
+ '.less-error-message ul, .less-error-message li {',
+ 'list-style-type: none;',
+ 'margin-right: 15px;',
+ 'padding: 4px 0;',
+ 'margin: 0;',
+ '}',
+ '.less-error-message label {',
+ 'font-size: 12px;',
+ 'margin-right: 15px;',
+ 'padding: 4px 0;',
+ 'color: #cc7777;',
+ '}',
+ '.less-error-message pre {',
+ 'color: #dd6666;',
+ 'padding: 4px 0;',
+ 'margin: 0;',
+ 'display: inline-block;',
+ '}',
+ '.less-error-message pre.line {',
+ 'color: #ff0000;',
+ '}',
+ '.less-error-message h3 {',
+ 'font-size: 20px;',
+ 'font-weight: bold;',
+ 'padding: 15px 0 5px 0;',
+ 'margin: 0;',
+ '}',
+ '.less-error-message a {',
+ 'color: #10a',
+ '}',
+ '.less-error-message .error {',
+ 'color: red;',
+ 'font-weight: bold;',
+ 'padding-bottom: 2px;',
+ 'border-bottom: 1px dashed red;',
+ '}'
+ ].join('\n'), { title: 'error-message' });
+
+ elem.style.cssText = [
+ "font-family: Arial, sans-serif",
+ "border: 1px solid #e00",
+ "background-color: #eee",
+ "border-radius: 5px",
+ "-webkit-border-radius: 5px",
+ "-moz-border-radius: 5px",
+ "color: #e00",
+ "padding: 15px",
+ "margin-bottom: 15px"
+ ].join(';');
+
+ if (options.env === 'development') {
+ timer = setInterval(function () {
+ var document = window.document,
+ body = document.body;
+ if (body) {
+ if (document.getElementById(id)) {
+ body.replaceChild(elem, document.getElementById(id));
+ } else {
+ body.insertBefore(elem, body.firstChild);
+ }
+ clearInterval(timer);
+ }
+ }, 10);
+ }
+ }
+
+ function error(e, rootHref) {
+ if (!options.errorReporting || options.errorReporting === "html") {
+ errorHTML(e, rootHref);
+ } else if (options.errorReporting === "console") {
+ errorConsole(e, rootHref);
+ } else if (typeof options.errorReporting === 'function') {
+ options.errorReporting("add", e, rootHref);
+ }
+ }
+
+ function removeErrorHTML(path) {
+ var node = window.document.getElementById('less-error-message:' + utils.extractId(path));
+ if (node) {
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ function removeErrorConsole(path) {
+ //no action
+ }
+
+ function removeError(path) {
+ if (!options.errorReporting || options.errorReporting === "html") {
+ removeErrorHTML(path);
+ } else if (options.errorReporting === "console") {
+ removeErrorConsole(path);
+ } else if (typeof options.errorReporting === 'function') {
+ options.errorReporting("remove", path);
+ }
+ }
+
+ function errorConsole(e, rootHref) {
+ var template = '{line} {content}';
+ var filename = e.filename || rootHref;
+ var errors = [];
+ var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
+ " in " + filename + " ";
+
+ var errorline = function (e, i, classname) {
+ if (e.extract[i] !== undefined) {
+ errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
+ .replace(/\{class\}/, classname)
+ .replace(/\{content\}/, e.extract[i]));
+ }
+ };
+
+ if (e.extract) {
+ errorline(e, 0, '');
+ errorline(e, 1, 'line');
+ errorline(e, 2, '');
+ content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' +
+ errors.join('\n');
+ }
+ if (e.stack && (e.extract || options.logLevel >= 4)) {
+ content += '\nStack Trace\n' + e.stack;
+ }
+ less.logger.error(content);
+ }
+
+ return {
+ add: error,
+ remove: removeError
+ };
+};
--- /dev/null
+/*global window, XMLHttpRequest */
+
+module.exports = function(options, logger) {
+
+ var AbstractFileManager = require("../less/environment/abstract-file-manager.js");
+
+ var fileCache = {};
+
+ //TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
+
+ function getXMLHttpRequest() {
+ if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
+ return new XMLHttpRequest();
+ } else {
+ try {
+ /*global ActiveXObject */
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e) {
+ logger.error("browser doesn't support AJAX.");
+ return null;
+ }
+ }
+ }
+
+ var FileManager = function() {
+ };
+
+ FileManager.prototype = new AbstractFileManager();
+
+ FileManager.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() {
+ return true;
+ };
+ FileManager.prototype.join = function join(basePath, laterPath) {
+ if (!basePath) {
+ return laterPath;
+ }
+ return this.extractUrlParts(laterPath, basePath).path;
+ };
+ FileManager.prototype.doXHR = function doXHR(url, type, callback, errback) {
+
+ var xhr = getXMLHttpRequest();
+ var async = options.isFileProtocol ? options.fileAsync : options.async;
+
+ if (typeof xhr.overrideMimeType === 'function') {
+ xhr.overrideMimeType('text/css');
+ }
+ logger.debug("XHR: Getting '" + url + "'");
+ xhr.open('GET', url, async);
+ xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
+ xhr.send(null);
+
+ function handleResponse(xhr, callback, errback) {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ callback(xhr.responseText,
+ xhr.getResponseHeader("Last-Modified"));
+ } else if (typeof errback === 'function') {
+ errback(xhr.status, url);
+ }
+ }
+
+ if (options.isFileProtocol && !options.fileAsync) {
+ if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
+ callback(xhr.responseText);
+ } else {
+ errback(xhr.status, url);
+ }
+ } else if (async) {
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ handleResponse(xhr, callback, errback);
+ }
+ };
+ } else {
+ handleResponse(xhr, callback, errback);
+ }
+ };
+ FileManager.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return true;
+ };
+
+ FileManager.prototype.clearFileCache = function() {
+ fileCache = {};
+ };
+
+ FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment, callback) {
+ if (currentDirectory && !this.isPathAbsolute(filename)) {
+ filename = currentDirectory + filename;
+ }
+
+ options = options || {};
+
+ // sheet may be set to the stylesheet for the initial load or a collection of properties including
+ // some context variables for imports
+ var hrefParts = this.extractUrlParts(filename, window.location.href);
+ var href = hrefParts.url;
+
+ if (options.useFileCache && fileCache[href]) {
+ try {
+ var lessText = fileCache[href];
+ callback(null, { contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
+ } catch (e) {
+ callback({filename: href, message: "Error loading file " + href + " error was " + e.message});
+ }
+ return;
+ }
+
+ this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
+ // per file cache
+ fileCache[href] = data;
+
+ // Use remote copy (re-parse)
+ callback(null, { contents: data, filename: href, webInfo: { lastModified: lastModified }});
+ }, function doXHRError(status, url) {
+ callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href });
+ });
+ };
+
+ return FileManager;
+};
--- /dev/null
+//
+// index.js
+// Should expose the additional browser functions on to the less object
+//
+var addDataAttr = require("./utils").addDataAttr,
+ browser = require("./browser");
+
+module.exports = function(window, options) {
+ var document = window.document;
+ var less = require('../less')();
+ //module.exports = less;
+ less.options = options;
+ var environment = less.environment,
+ FileManager = require("./file-manager")(options, less.logger),
+ fileManager = new FileManager();
+ environment.addFileManager(fileManager);
+ less.FileManager = FileManager;
+
+ require("./log-listener")(less, options);
+ var errors = require("./error-reporting")(window, less, options);
+ var cache = less.cache = options.cache || require("./cache")(window, options, less.logger);
+
+ //Setup user functions
+ if (options.functions) {
+ less.functions.functionRegistry.addMultiple(options.functions);
+ }
+
+ var typePattern = /^text\/(x-)?less$/;
+
+ function postProcessCSS(styles) {
+ if (options.postProcessor && typeof options.postProcessor === 'function') {
+ styles = options.postProcessor.call(styles, styles) || styles;
+ }
+ return styles;
+ }
+
+ function clone(obj) {
+ var cloned = {};
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ cloned[prop] = obj[prop];
+ }
+ }
+ return cloned;
+ }
+
+ // only really needed for phantom
+ function bind(func, thisArg) {
+ var curryArgs = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0));
+ return func.apply(thisArg, args);
+ };
+ }
+
+ function loadStyles(modifyVars) {
+ var styles = document.getElementsByTagName('style'),
+ style;
+
+ for (var i = 0; i < styles.length; i++) {
+ style = styles[i];
+ if (style.type.match(typePattern)) {
+ var instanceOptions = clone(options);
+ instanceOptions.modifyVars = modifyVars;
+ var lessText = style.innerHTML || '';
+ instanceOptions.filename = document.location.href.replace(/#.*$/, '');
+
+ /*jshint loopfunc:true */
+ // use closure to store current style
+ less.render(lessText, instanceOptions,
+ bind(function(style, e, result) {
+ if (e) {
+ errors.add(e, "inline");
+ } else {
+ style.type = 'text/css';
+ if (style.styleSheet) {
+ style.styleSheet.cssText = result.css;
+ } else {
+ style.innerHTML = result.css;
+ }
+ }
+ }, null, style));
+ }
+ }
+ }
+
+ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
+
+ var instanceOptions = clone(options);
+ addDataAttr(instanceOptions, sheet);
+ instanceOptions.mime = sheet.type;
+
+ if (modifyVars) {
+ instanceOptions.modifyVars = modifyVars;
+ }
+
+ function loadInitialFileCallback(loadedFile) {
+
+ var data = loadedFile.contents,
+ path = loadedFile.filename,
+ webInfo = loadedFile.webInfo;
+
+ var newFileInfo = {
+ currentDirectory: fileManager.getPath(path),
+ filename: path,
+ rootFilename: path,
+ relativeUrls: instanceOptions.relativeUrls};
+
+ newFileInfo.entryPath = newFileInfo.currentDirectory;
+ newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory;
+
+ if (webInfo) {
+ webInfo.remaining = remaining;
+
+ if (!instanceOptions.modifyVars) {
+ var css = cache.getCSS(path, webInfo);
+ if (!reload && css) {
+ webInfo.local = true;
+ callback(null, css, data, sheet, webInfo, path);
+ return;
+ }
+ }
+ }
+
+ //TODO add tests around how this behaves when reloading
+ errors.remove(path);
+
+ instanceOptions.rootFileInfo = newFileInfo;
+ less.render(data, instanceOptions, function(e, result) {
+ if (e) {
+ e.href = path;
+ callback(e);
+ } else {
+ result.css = postProcessCSS(result.css);
+ if (!instanceOptions.modifyVars) {
+ cache.setCSS(sheet.href, webInfo.lastModified, result.css);
+ }
+ callback(null, result.css, data, sheet, webInfo, path);
+ }
+ });
+ }
+
+ fileManager.loadFile(sheet.href, null, instanceOptions, environment, function(e, loadedFile) {
+ if (e) {
+ callback(e);
+ return;
+ }
+ loadInitialFileCallback(loadedFile);
+ });
+ }
+
+ function loadStyleSheets(callback, reload, modifyVars) {
+ for (var i = 0; i < less.sheets.length; i++) {
+ loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars);
+ }
+ }
+
+ function initRunningMode() {
+ if (less.env === 'development') {
+ less.watchTimer = setInterval(function () {
+ if (less.watchMode) {
+ fileManager.clearFileCache();
+ loadStyleSheets(function (e, css, _, sheet, webInfo) {
+ if (e) {
+ errors.add(e, e.href || sheet.href);
+ } else if (css) {
+ browser.createCSS(window.document, css, sheet);
+ }
+ });
+ }
+ }, options.poll);
+ }
+ }
+
+ //
+ // Watch mode
+ //
+ less.watch = function () {
+ if (!less.watchMode ) {
+ less.env = 'development';
+ initRunningMode();
+ }
+ this.watchMode = true;
+ return true;
+ };
+
+ less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; };
+
+ //
+ // Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
+ //
+ less.registerStylesheets = function() {
+ return new Promise(function(resolve, reject) {
+ var links = document.getElementsByTagName('link');
+ less.sheets = [];
+
+ for (var i = 0; i < links.length; i++) {
+ if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
+ (links[i].type.match(typePattern)))) {
+ less.sheets.push(links[i]);
+ }
+ }
+
+ resolve();
+ });
+ };
+
+ //
+ // With this function, it's possible to alter variables and re-render
+ // CSS without reloading less-files
+ //
+ less.modifyVars = function(record) {
+ return less.refresh(true, record, false);
+ };
+
+ less.refresh = function (reload, modifyVars, clearFileCache) {
+ if ((reload || clearFileCache) && clearFileCache !== false) {
+ fileManager.clearFileCache();
+ }
+ return new Promise(function (resolve, reject) {
+ var startTime, endTime, totalMilliseconds;
+ startTime = endTime = new Date();
+
+ loadStyleSheets(function (e, css, _, sheet, webInfo) {
+ if (e) {
+ errors.add(e, e.href || sheet.href);
+ reject(e);
+ return;
+ }
+ if (webInfo.local) {
+ less.logger.info("loading " + sheet.href + " from cache.");
+ } else {
+ less.logger.info("rendered " + sheet.href + " successfully.");
+ }
+ browser.createCSS(window.document, css, sheet);
+ less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms');
+ if (webInfo.remaining === 0) {
+ totalMilliseconds = new Date() - startTime;
+ less.logger.info("less has finished. css generated in " + totalMilliseconds + 'ms');
+ resolve({
+ startTime: startTime,
+ endTime: endTime,
+ totalMilliseconds: totalMilliseconds,
+ sheets: less.sheets.length
+ });
+ }
+ endTime = new Date();
+ }, reload, modifyVars);
+
+ loadStyles(modifyVars);
+ });
+ };
+
+ less.refreshStyles = loadStyles;
+ return less;
+};
--- /dev/null
+module.exports = function(less, options) {
+
+ var logLevel_debug = 4,
+ logLevel_info = 3,
+ logLevel_warn = 2,
+ logLevel_error = 1;
+
+ // The amount of logging in the javascript console.
+ // 3 - Debug, information and errors
+ // 2 - Information and errors
+ // 1 - Errors
+ // 0 - None
+ // Defaults to 2
+ options.logLevel = typeof options.logLevel !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_info : logLevel_error);
+
+ if (!options.loggers) {
+ options.loggers = [{
+ debug: function(msg) {
+ if (options.logLevel >= logLevel_debug) {
+ console.log(msg);
+ }
+ },
+ info: function(msg) {
+ if (options.logLevel >= logLevel_info) {
+ console.log(msg);
+ }
+ },
+ warn: function(msg) {
+ if (options.logLevel >= logLevel_warn) {
+ console.warn(msg);
+ }
+ },
+ error: function(msg) {
+ if (options.logLevel >= logLevel_error) {
+ console.error(msg);
+ }
+ }
+ }];
+ }
+ for (var i = 0; i < options.loggers.length; i++) {
+ less.logger.addListener(options.loggers[i]);
+ }
+};
--- /dev/null
+module.exports = {
+ extractId: function(href) {
+ return href.replace(/^[a-z-]+:\/+?[^\/]+/, '') // Remove protocol & domain
+ .replace(/[\?\&]livereload=\w+/, '') // Remove LiveReload cachebuster
+ .replace(/^\//, '') // Remove root /
+ .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension
+ .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
+ .replace(/\./g, ':'); // Replace dots with colons(for valid id)
+ },
+ addDataAttr: function(options, tag) {
+ for (var opt in tag.dataset) {
+ if (tag.dataset.hasOwnProperty(opt)) {
+ if (opt === "env" || opt === "dumpLineNumbers" || opt === "rootpath" || opt === "errorReporting") {
+ options[opt] = tag.dataset[opt];
+ } else {
+ try {
+ options[opt] = JSON.parse(tag.dataset[opt]);
+ }
+ catch(_) {}
+ }
+ }
+ }
+ }
+};
--- /dev/null
+module.exports = {
+ encodeBase64: function encodeBase64(str) {
+ return new Buffer(str).toString('base64');
+ },
+ mimeLookup: function (filename) {
+ return require('mime').lookup(filename);
+ },
+ charsetLookup: function (mime) {
+ return require('mime').charsets.lookup(mime);
+ },
+ getSourceMapGenerator: function getSourceMapGenerator() {
+ return require("source-map").SourceMapGenerator;
+ }
+};
--- /dev/null
+var path = require('path'),
+ fs = require('./fs'),
+ PromiseConstructor,
+ AbstractFileManager = require("../less/environment/abstract-file-manager.js");
+
+try {
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
+} catch(e) {
+}
+
+var FileManager = function() {
+};
+
+FileManager.prototype = new AbstractFileManager();
+
+FileManager.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return true;
+};
+FileManager.prototype.supportsSync = function(filename, currentDirectory, options, environment) {
+ return true;
+};
+
+FileManager.prototype.loadFile = function(filename, currentDirectory, options, environment, callback) {
+ var fullFilename,
+ data,
+ isAbsoluteFilename = this.isPathAbsolute(filename),
+ filenamesTried = [];
+
+ options = options || {};
+
+ if (options.syncImport || !PromiseConstructor) {
+ data = this.loadFileSync(filename, currentDirectory, options, environment, 'utf-8');
+ callback(data.error, data);
+ return;
+ }
+
+ var paths = isAbsoluteFilename ? [""] : [currentDirectory];
+ if (options.paths) { paths.push.apply(paths, options.paths); }
+ if (!isAbsoluteFilename && paths.indexOf('.') === -1) { paths.push('.'); }
+
+ // promise is guarenteed to be asyncronous
+ // which helps as it allows the file handle
+ // to be closed before it continues with the next file
+ return new PromiseConstructor(function(fulfill, reject) {
+ (function tryPathIndex(i) {
+ if (i < paths.length) {
+ fullFilename = filename;
+ if (paths[i]) {
+ fullFilename = path.join(paths[i], fullFilename);
+ }
+ fs.stat(fullFilename, function (err) {
+ if (err) {
+ filenamesTried.push(fullFilename);
+ tryPathIndex(i + 1);
+ } else {
+ fs.readFile(fullFilename, 'utf-8', function(e, data) {
+ if (e) { reject(e); return; }
+
+ fulfill({ contents: data, filename: fullFilename});
+ });
+ }
+ });
+ } else {
+ reject({ type: 'File', message: "'" + filename + "' wasn't found. Tried - " + filenamesTried.join(",") });
+ }
+ }(0));
+ });
+};
+
+FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment, encoding) {
+ var fullFilename, paths, filenamesTried = [], isAbsoluteFilename = this.isPathAbsolute(filename) , data;
+ options = options || {};
+
+ paths = isAbsoluteFilename ? [""] : [currentDirectory];
+ if (options.paths) {
+ paths.push.apply(paths, options.paths);
+ }
+ if (!isAbsoluteFilename && paths.indexOf('.') === -1) {
+ paths.push('.');
+ }
+
+ var err, result;
+ for (var i = 0; i < paths.length; i++) {
+ try {
+ fullFilename = filename;
+ if (paths[i]) {
+ fullFilename = path.join(paths[i], fullFilename);
+ }
+ filenamesTried.push(fullFilename);
+ fs.statSync(fullFilename);
+ break;
+ } catch (e) {
+ fullFilename = null;
+ }
+ }
+
+ if (!fullFilename) {
+ err = { type: 'File', message: "'" + filename + "' wasn't found. Tried - " + filenamesTried.join(",") };
+ result = { error: err };
+ } else {
+ data = fs.readFileSync(fullFilename, encoding);
+ result = { contents: data, filename: fullFilename};
+ }
+
+ return result;
+};
+
+module.exports = FileManager;
--- /dev/null
+var fs;
+try
+{
+ fs = require("graceful-fs");
+}
+catch(e)
+{
+ fs = require("fs");
+}
+module.exports = fs;
--- /dev/null
+var Dimension = require("../less/tree/dimension"),
+ Expression = require("../less/tree/expression"),
+ functionRegistry = require("./../less/functions/function-registry"),
+ path = require("path");
+
+function imageSize(filePathNode) {
+ var filePath = filePathNode.value;
+ var currentDirectory = filePathNode.currentFileInfo.relativeUrls ?
+ filePathNode.currentFileInfo.currentDirectory : filePathNode.currentFileInfo.entryPath;
+
+ var sizeOf = require('image-size');
+ filePath = path.join(currentDirectory, filePath);
+ return sizeOf(filePath);
+}
+
+var imageFunctions = {
+ "image-size": function(filePathNode) {
+ var size = imageSize(filePathNode);
+ return new Expression([
+ new Dimension(size.width, "px"),
+ new Dimension(size.height, "px")
+ ]);
+ },
+ "image-width": function(filePathNode) {
+ var size = imageSize(filePathNode);
+ return new Dimension(size.width, "px");
+ },
+ "image-height": function(filePathNode) {
+ var size = imageSize(filePathNode);
+ return new Dimension(size.height, "px");
+ }
+};
+
+functionRegistry.addMultiple(imageFunctions);
\ No newline at end of file
--- /dev/null
+var environment = require("./environment"),
+ FileManager = require("./file-manager"),
+ UrlFileManager = require("./url-file-manager"),
+ createFromEnvironment = require("../less"),
+ less = createFromEnvironment(environment, [new FileManager(), new UrlFileManager()]),
+ lesscHelper = require('./lessc-helper');
+
+// allow people to create less with their own environment
+less.createFromEnvironment = createFromEnvironment;
+less.lesscHelper = lesscHelper;
+less.PluginLoader = require("./plugin-loader");
+less.fs = require("./fs");
+less.FileManager = FileManager;
+less.UrlFileManager = UrlFileManager;
+less.formatError = function(ctx, options) {
+ options = options || {};
+
+ var message = "";
+ var extract = ctx.extract;
+ var error = [];
+ var stylize = options.color ? lesscHelper.stylize : function (str) { return str; };
+
+ // only output a stack if it isn't a less error
+ if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); }
+
+ if (!ctx.hasOwnProperty('index') || !extract) {
+ return ctx.stack || ctx.message;
+ }
+
+ if (typeof extract[0] === 'string') {
+ error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));
+ }
+
+ if (typeof extract[1] === 'string') {
+ var errorTxt = ctx.line + ' ';
+ if (extract[1]) {
+ errorTxt += extract[1].slice(0, ctx.column) +
+ stylize(stylize(stylize(extract[1].substr(ctx.column, 1), 'bold') +
+ extract[1].slice(ctx.column + 1), 'red'), 'inverse');
+ }
+ error.push(errorTxt);
+ }
+
+ if (typeof extract[2] === 'string') {
+ error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
+ }
+ error = error.join('\n') + stylize('', 'reset') + '\n';
+
+ message += stylize(ctx.type + 'Error: ' + ctx.message, 'red');
+ if (ctx.filename) {
+ message += stylize(' in ', 'red') + ctx.filename +
+ stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey');
+ }
+
+ message += '\n' + error;
+
+ if (ctx.callLine) {
+ message += stylize('from ', 'red') + (ctx.filename || '') + '/n';
+ message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n';
+ }
+
+ return message;
+};
+
+less.writeError = function (ctx, options) {
+ options = options || {};
+ if (options.silent) { return; }
+ console.error(less.formatError(ctx, options));
+};
+
+// provide image-size functionality
+require('./image-size');
+
+module.exports = less;
--- /dev/null
+// lessc_helper.js
+//
+// helper functions for lessc
+var lessc_helper = {
+
+ //Stylize a string
+ stylize : function(str, style) {
+ var styles = {
+ 'reset' : [0, 0],
+ 'bold' : [1, 22],
+ 'inverse' : [7, 27],
+ 'underline' : [4, 24],
+ 'yellow' : [33, 39],
+ 'green' : [32, 39],
+ 'red' : [31, 39],
+ 'grey' : [90, 39]
+ };
+ return '\033[' + styles[style][0] + 'm' + str +
+ '\033[' + styles[style][1] + 'm';
+ },
+
+ //Print command line options
+ printUsage: function() {
+ console.log("usage: lessc [option option=parameter ...] <source> [destination]");
+ console.log("");
+ console.log("If source is set to `-' (dash or hyphen-minus), input is read from stdin.");
+ console.log("");
+ console.log("options:");
+ console.log(" -h, --help Prints help (this message) and exit.");
+ console.log(" --include-path=PATHS Sets include paths. Separated by `:'. Use `;' on Windows.");
+ console.log(" -M, --depends Outputs a makefile import dependency list to stdout.");
+ console.log(" --no-color Disables colorized output.");
+ console.log(" --no-ie-compat Disables IE compatibility checks.");
+ console.log(" --no-js Disables JavaScript in less files");
+ console.log(" -l, --lint Syntax check only (lint).");
+ console.log(" -s, --silent Suppresses output of error messages.");
+ console.log(" --strict-imports Forces evaluation of imports.");
+ console.log(" --insecure Allows imports from insecure https hosts.");
+ console.log(" -v, --version Prints version number and exit.");
+ console.log(" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map).");
+ console.log(" --source-map-rootpath=X Adds this path onto the sourcemap filename and less file paths.");
+ console.log(" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory.");
+ console.log(" --source-map-less-inline Puts the less files into the map instead of referencing them.");
+ console.log(" --source-map-map-inline Puts the map (and any less files) as a base64 data uri into the output css file.");
+ console.log(" --source-map-url=URL Sets a custom URL to map file, for sourceMappingURL comment");
+ console.log(" in generated CSS file.");
+ console.log(" -rp, --rootpath=URL Sets rootpath for url rewriting in relative imports and urls");
+ console.log(" Works with or without the relative-urls option.");
+ console.log(" -ru, --relative-urls Re-writes relative urls to the base less file.");
+ console.log(" -sm=on|off Turns on or off strict math, where in strict mode, math.");
+ console.log(" --strict-math=on|off Requires brackets. This option may default to on and then");
+ console.log(" be removed in the future.");
+ console.log(" -su=on|off Allows mixed units, e.g. 1px+1em or 1px*1px which have units");
+ console.log(" --strict-units=on|off that cannot be represented.");
+ console.log(" --global-var='VAR=VALUE' Defines a variable that can be referenced by the file.");
+ console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file.");
+ console.log(" --url-args='QUERYSTRING' Adds params into url tokens (e.g. 42, cb=42 or 'a=1&b=2')");
+ console.log(" --plugin=PLUGIN=OPTIONS Loads a plugin. You can also omit the --plugin= if the plugin begins");
+ console.log(" less-plugin. E.g. the clean css plugin is called less-plugin-clean-css");
+ console.log(" once installed (npm install less-plugin-clean-css), use either with");
+ console.log(" --plugin=less-plugin-clean-css or just --clean-css");
+ console.log(" specify options afterwards e.g. --plugin=less-plugin-clean-css=\"advanced\"");
+ console.log(" or --clean-css=\"advanced\"");
+ console.log("");
+ console.log("-------------------------- Deprecated ----------------");
+ console.log(" --line-numbers=TYPE Outputs filename and line numbers.");
+ console.log(" TYPE can be either 'comments', which will output");
+ console.log(" the debug info within comments, 'mediaquery'");
+ console.log(" that will output the information within a fake");
+ console.log(" media query which is compatible with the SASS");
+ console.log(" format, and 'all' which will do both.");
+ console.log(" --verbose Be verbose.");
+ console.log(" -x, --compress Compresses output by removing some whitespaces.");
+ console.log(" We recommend you use a dedicated minifer like less-plugin-clean-css");
+ console.log("");
+ console.log("Report bugs to: http://github.com/less/less.js/issues");
+ console.log("Home page: <http://lesscss.org/>");
+ }
+};
+
+// Exports helper functions
+for (var h in lessc_helper) { if (lessc_helper.hasOwnProperty(h)) { exports[h] = lessc_helper[h]; }}
--- /dev/null
+var path = require("path");
+/**
+ * Node Plugin Loader
+ */
+var PluginLoader = function(less) {
+ this.less = less;
+};
+PluginLoader.prototype.tryLoadPlugin = function(name, argument) {
+ var plugin = this.tryRequirePlugin(name);
+ if (plugin) {
+ // support plugins being a function
+ // so that the plugin can be more usable programmatically
+ if (typeof plugin === "function") {
+ plugin = new plugin();
+ }
+ if (plugin.minVersion) {
+ if (this.compareVersion(plugin.minVersion, this.less.version) < 0) {
+ console.log("plugin " + name + " requires version " + this.versionToString(plugin.minVersion));
+ return null;
+ }
+ }
+ if (argument) {
+ if (!plugin.setOptions) {
+ console.log("options have been provided but the plugin " + name + "does not support any options");
+ return null;
+ }
+ try {
+ plugin.setOptions(argument);
+ }
+ catch(e) {
+ console.log("Error setting options on plugin " + name);
+ console.log(e.message);
+ return null;
+ }
+ }
+ return plugin;
+ }
+ return null;
+};
+PluginLoader.prototype.compareVersion = function(aVersion, bVersion) {
+ for (var i = 0; i < aVersion.length; i++) {
+ if (aVersion[i] !== bVersion[i]) {
+ return parseInt(aVersion[i]) > parseInt(bVersion[i]) ? -1 : 1;
+ }
+ }
+ return 0;
+};
+PluginLoader.prototype.versionToString = function(version) {
+ var versionString = "";
+ for (var i = 0; i < version.length; i++) {
+ versionString += (versionString ? "." : "") + version[i];
+ }
+ return versionString;
+};
+PluginLoader.prototype.tryRequirePlugin = function(name) {
+ // is at the same level as the less.js module
+ try {
+ return require("../../../" + name);
+ }
+ catch(e) {
+ }
+ // is installed as a sub dependency of the current folder
+ try {
+ return require(path.join(process.cwd(), "node_modules", name));
+ }
+ catch(e) {
+ }
+ // is referenced relative to the current directory
+ try {
+ return require(path.join(process.cwd(), name));
+ }
+ catch(e) {
+ }
+ // unlikely - would have to be a dependency of where this code was running (less.js)...
+ if (name[0] !== '.') {
+ try {
+ return require(name);
+ }
+ catch(e) {
+ }
+ }
+};
+PluginLoader.prototype.printUsage = function(plugins) {
+ for (var i = 0; i < plugins.length; i++) {
+ var plugin = plugins[i];
+ if (plugin.printUsage) {
+ plugin.printUsage();
+ }
+ }
+};
+module.exports = PluginLoader;
--- /dev/null
+var isUrlRe = /^(?:https?:)?\/\//i,
+ url = require('url'),
+ request,
+ PromiseConstructor,
+ AbstractFileManager = require("../less/environment/abstract-file-manager.js"),
+ logger = require("../less/logger");
+
+var UrlFileManager = function() {
+};
+
+UrlFileManager.prototype = new AbstractFileManager();
+
+UrlFileManager.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return isUrlRe.test( filename ) || isUrlRe.test(currentDirectory);
+};
+
+UrlFileManager.prototype.loadFile = function(filename, currentDirectory, options, environment) {
+ if (!PromiseConstructor) {
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
+ }
+ return new PromiseConstructor(function(fulfill, reject) {
+ if (request === undefined) {
+ try { request = require('request'); }
+ catch(e) { request = null; }
+ }
+ if (!request) {
+ reject({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" });
+ return;
+ }
+
+ var urlStr = isUrlRe.test( filename ) ? filename : url.resolve(currentDirectory, filename),
+ urlObj = url.parse(urlStr);
+
+ if (!urlObj.protocol) {
+ urlObj.protocol = "http";
+ urlStr = urlObj.format();
+ }
+
+ request.get({uri: urlStr, strictSSL: !options.insecure }, function (error, res, body) {
+ if (error) {
+ reject({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n " + error + "\n" });
+ return;
+ }
+ if (res && res.statusCode === 404) {
+ reject({ type: 'File', message: "resource '" + urlStr + "' was not found\n" });
+ return;
+ }
+ if (!body) {
+ logger.warn('Warning: Empty body (HTTP ' + res.statusCode + ') returned by "' + urlStr + '"');
+ }
+ fulfill({ contents: body, filename: urlStr });
+ });
+ });
+};
+
+module.exports = UrlFileManager;
--- /dev/null
+/*jshint rhino:true, unused: false */
+/*global name:true, less, loadStyleSheet, os */
+
+function formatError(ctx, options) {
+ options = options || {};
+
+ var message = "";
+ var extract = ctx.extract;
+ var error = [];
+
+// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };
+ var stylize = function (str) { return str; };
+
+ // only output a stack if it isn't a less error
+ if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); }
+
+ if (!ctx.hasOwnProperty('index') || !extract) {
+ return ctx.stack || ctx.message;
+ }
+
+ if (typeof extract[0] === 'string') {
+ error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));
+ }
+
+ if (typeof extract[1] === 'string') {
+ var errorTxt = ctx.line + ' ';
+ if (extract[1]) {
+ errorTxt += extract[1].slice(0, ctx.column) +
+ stylize(stylize(stylize(extract[1][ctx.column], 'bold') +
+ extract[1].slice(ctx.column + 1), 'red'), 'inverse');
+ }
+ error.push(errorTxt);
+ }
+
+ if (typeof extract[2] === 'string') {
+ error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
+ }
+ error = error.join('\n') + stylize('', 'reset') + '\n';
+
+ message += stylize(ctx.type + 'Error: ' + ctx.message, 'red');
+ if (ctx.filename) {
+ message += stylize(' in ', 'red') + ctx.filename +
+ stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey');
+ }
+
+ message += '\n' + error;
+
+ if (ctx.callLine) {
+ message += stylize('from ', 'red') + (ctx.filename || '') + '/n';
+ message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n';
+ }
+
+ return message;
+}
+
+function writeError(ctx, options) {
+ options = options || {};
+ if (options.silent) { return; }
+ var message = formatError(ctx, options);
+ throw new Error(message);
+}
+
+function loadStyleSheet(sheet, callback, reload, remaining) {
+ var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')),
+ sheetName = name.slice(0, endOfPath + 1) + sheet.href,
+ contents = sheet.contents || {},
+ input = readFile(sheetName);
+
+ input = input.replace(/^\xEF\xBB\xBF/, '');
+
+ contents[sheetName] = input;
+
+ var parser = new less.Parser({
+ paths: [sheet.href.replace(/[\w\.-]+$/, '')],
+ contents: contents
+ });
+ parser.parse(input, function (e, root) {
+ if (e) {
+ return writeError(e);
+ }
+ try {
+ callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName);
+ } catch(e) {
+ writeError(e);
+ }
+ });
+}
+
+less.Parser.fileLoader = function (file, currentFileInfo, callback, env) {
+
+ var href = file;
+ if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) {
+ href = less.modules.path.join(currentFileInfo.currentDirectory, file);
+ }
+
+ var path = less.modules.path.dirname(href);
+
+ var newFileInfo = {
+ currentDirectory: path + '/',
+ filename: href
+ };
+
+ if (currentFileInfo) {
+ newFileInfo.entryPath = currentFileInfo.entryPath;
+ newFileInfo.rootpath = currentFileInfo.rootpath;
+ newFileInfo.rootFilename = currentFileInfo.rootFilename;
+ newFileInfo.relativeUrls = currentFileInfo.relativeUrls;
+ } else {
+ newFileInfo.entryPath = path;
+ newFileInfo.rootpath = less.rootpath || path;
+ newFileInfo.rootFilename = href;
+ newFileInfo.relativeUrls = env.relativeUrls;
+ }
+
+ var j = file.lastIndexOf('/');
+ if (newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
+ var relativeSubDirectory = file.slice(0, j + 1);
+ newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file
+ }
+ newFileInfo.currentDirectory = path;
+ newFileInfo.filename = href;
+
+ var data = null;
+ try {
+ data = readFile(href);
+ } catch (e) {
+ callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" });
+ return;
+ }
+
+ try {
+ callback(null, data, href, newFileInfo, { lastModified: 0 });
+ } catch (e) {
+ callback(e, null, href);
+ }
+};
+
+function writeFile(filename, content) {
+ var fstream = new java.io.FileWriter(filename);
+ var out = new java.io.BufferedWriter(fstream);
+ out.write(content);
+ out.close();
+}
+
+// Command line integration via Rhino
+(function (args) {
+
+ var options = {
+ depends: false,
+ compress: false,
+ cleancss: false,
+ max_line_len: -1,
+ silent: false,
+ verbose: false,
+ lint: false,
+ paths: [],
+ color: true,
+ strictImports: false,
+ rootpath: '',
+ relativeUrls: false,
+ ieCompat: true,
+ strictMath: false,
+ strictUnits: false
+ };
+ var continueProcessing = true,
+ currentErrorcode;
+
+ var checkArgFunc = function(arg, option) {
+ if (!option) {
+ print(arg + " option requires a parameter");
+ continueProcessing = false;
+ return false;
+ }
+ return true;
+ };
+
+ var checkBooleanArg = function(arg) {
+ var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg);
+ if (!onOff) {
+ print(" unable to parse " + arg + " as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
+ continueProcessing = false;
+ return false;
+ }
+ return Boolean(onOff[2]);
+ };
+
+ var warningMessages = "";
+ var sourceMapFileInline = false;
+
+ args = args.filter(function (arg) {
+ var match = arg.match(/^-I(.+)$/);
+
+ if (match) {
+ options.paths.push(match[1]);
+ return false;
+ }
+
+ match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
+ if (match) {
+ arg = match[1];
+ }
+ else {
+ return arg;
+ }
+
+ switch (arg) {
+ case 'v':
+ case 'version':
+ console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
+ continueProcessing = false;
+ break;
+ case 'verbose':
+ options.verbose = true;
+ break;
+ case 's':
+ case 'silent':
+ options.silent = true;
+ break;
+ case 'l':
+ case 'lint':
+ options.lint = true;
+ break;
+ case 'strict-imports':
+ options.strictImports = true;
+ break;
+ case 'h':
+ case 'help':
+ //TODO
+// require('../lib/less/lessc_helper').printUsage();
+ continueProcessing = false;
+ break;
+ case 'x':
+ case 'compress':
+ options.compress = true;
+ break;
+ case 'M':
+ case 'depends':
+ options.depends = true;
+ break;
+ case 'yui-compress':
+ warningMessages += "yui-compress option has been removed. assuming clean-css.";
+ options.cleancss = true;
+ break;
+ case 'clean-css':
+ options.cleancss = true;
+ break;
+ case 'max-line-len':
+ if (checkArgFunc(arg, match[2])) {
+ options.maxLineLen = parseInt(match[2], 10);
+ if (options.maxLineLen <= 0) {
+ options.maxLineLen = -1;
+ }
+ }
+ break;
+ case 'no-color':
+ options.color = false;
+ break;
+ case 'no-ie-compat':
+ options.ieCompat = false;
+ break;
+ case 'no-js':
+ options.javascriptEnabled = false;
+ break;
+ case 'include-path':
+ if (checkArgFunc(arg, match[2])) {
+ options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':')
+ .map(function(p) {
+ if (p) {
+// return path.resolve(process.cwd(), p);
+ return p;
+ }
+ });
+ }
+ break;
+ case 'line-numbers':
+ if (checkArgFunc(arg, match[2])) {
+ options.dumpLineNumbers = match[2];
+ }
+ break;
+ case 'source-map':
+ if (!match[2]) {
+ options.sourceMap = true;
+ } else {
+ options.sourceMap = match[2];
+ }
+ break;
+ case 'source-map-rootpath':
+ if (checkArgFunc(arg, match[2])) {
+ options.sourceMapRootpath = match[2];
+ }
+ break;
+ case 'source-map-basepath':
+ if (checkArgFunc(arg, match[2])) {
+ options.sourceMapBasepath = match[2];
+ }
+ break;
+ case 'source-map-map-inline':
+ sourceMapFileInline = true;
+ options.sourceMap = true;
+ break;
+ case 'source-map-less-inline':
+ options.outputSourceFiles = true;
+ break;
+ case 'source-map-url':
+ if (checkArgFunc(arg, match[2])) {
+ options.sourceMapURL = match[2];
+ }
+ break;
+ case 'source-map-output-map-file':
+ if (checkArgFunc(arg, match[2])) {
+ options.writeSourceMap = function(sourceMapContent) {
+ writeFile(match[2], sourceMapContent);
+ };
+ }
+ break;
+ case 'rp':
+ case 'rootpath':
+ if (checkArgFunc(arg, match[2])) {
+ options.rootpath = match[2].replace(/\\/g, '/');
+ }
+ break;
+ case "ru":
+ case "relative-urls":
+ options.relativeUrls = true;
+ break;
+ case "sm":
+ case "strict-math":
+ if (checkArgFunc(arg, match[2])) {
+ options.strictMath = checkBooleanArg(match[2]);
+ }
+ break;
+ case "su":
+ case "strict-units":
+ if (checkArgFunc(arg, match[2])) {
+ options.strictUnits = checkBooleanArg(match[2]);
+ }
+ break;
+ default:
+ console.log('invalid option ' + arg);
+ continueProcessing = false;
+ }
+ });
+
+ if (!continueProcessing) {
+ return;
+ }
+
+ var name = args[0];
+ if (name && name != '-') {
+// name = path.resolve(process.cwd(), name);
+ }
+ var output = args[1];
+ var outputbase = args[1];
+ if (output) {
+ options.sourceMapOutputFilename = output;
+// output = path.resolve(process.cwd(), output);
+ if (warningMessages) {
+ console.log(warningMessages);
+ }
+ }
+
+// options.sourceMapBasepath = process.cwd();
+// options.sourceMapBasepath = '';
+
+ if (options.sourceMap === true) {
+ console.log("output: " + output);
+ if (!output && !sourceMapFileInline) {
+ console.log("the sourcemap option only has an optional filename if the css filename is given");
+ return;
+ }
+ options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map";
+ options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename);
+ } else if (options.sourceMap) {
+ options.sourceMapOutputFilename = options.sourceMap;
+ }
+
+ if (!name) {
+ console.log("lessc: no inout files");
+ console.log("");
+ // TODO
+// require('../lib/less/lessc_helper').printUsage();
+ currentErrorcode = 1;
+ return;
+ }
+
+// var ensureDirectory = function (filepath) {
+// var dir = path.dirname(filepath),
+// cmd,
+// existsSync = fs.existsSync || path.existsSync;
+// if (!existsSync(dir)) {
+// if (mkdirp === undefined) {
+// try {mkdirp = require('mkdirp');}
+// catch(e) { mkdirp = null; }
+// }
+// cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
+// cmd(dir);
+// }
+// };
+
+ if (options.depends) {
+ if (!outputbase) {
+ console.log("option --depends requires an output path to be specified");
+ return;
+ }
+ console.log(outputbase + ": ");
+ }
+
+ if (!name) {
+ console.log('No files present in the fileset');
+ quit(1);
+ }
+
+ var input = null;
+ try {
+ input = readFile(name, 'utf-8');
+
+ } catch (e) {
+ console.log('lesscss: couldn\'t open file ' + name);
+ quit(1);
+ }
+
+ options.filename = name;
+ var result;
+ try {
+ var parser = new less.Parser(options);
+ parser.parse(input, function (e, root) {
+ if (e) {
+ writeError(e, options);
+ quit(1);
+ } else {
+ result = root.toCSS(options);
+ if (output) {
+ writeFile(output, result);
+ console.log("Written to " + output);
+ } else {
+ print(result);
+ }
+ quit(0);
+ }
+ });
+ }
+ catch(e) {
+ writeError(e, options);
+ quit(1);
+ }
+}(arguments));
--- /dev/null
+var contexts = {};
+module.exports = contexts;
+
+var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
+ if (!original) { return; }
+
+ for (var i = 0; i < propertiesToCopy.length; i++) {
+ if (original.hasOwnProperty(propertiesToCopy[i])) {
+ destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
+ }
+ }
+};
+
+/*
+ parse is used whilst parsing
+ */
+var parseCopyProperties = [
+ // options
+ 'paths', // option - unmodified - paths to search for imports on
+ 'relativeUrls', // option - whether to adjust URL's to be relative
+ 'rootpath', // option - rootpath to append to URL's
+ 'strictImports', // option -
+ 'insecure', // option - whether to allow imports from insecure ssl hosts
+ 'dumpLineNumbers', // option - whether to dump line numbers
+ 'compress', // option - whether to compress
+ 'syncImport', // option - whether to import synchronously
+ 'chunkInput', // option - whether to chunk input. more performant but causes parse issues.
+ 'mime', // browser only - mime type for sheet import
+ 'useFileCache', // browser only - whether to use the per file session cache
+ // context
+ 'processImports', // option & context - whether to process imports. if false then imports will not be imported.
+ // Used by the import manager to stop multiple import visitors being created.
+ 'reference', // Used to indicate that the contents are imported by reference
+ 'pluginManager' // Used as the plugin manager for the session
+];
+
+contexts.Parse = function(options) {
+ copyFromOriginal(options, this, parseCopyProperties);
+
+ if (typeof this.paths === "string") { this.paths = [this.paths]; }
+};
+
+var evalCopyProperties = [
+ 'paths', // additional include paths
+ 'compress', // whether to compress
+ 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
+ 'strictMath', // whether math has to be within parenthesis
+ 'strictUnits', // whether units need to evaluate correctly
+ 'sourceMap', // whether to output a source map
+ 'importMultiple', // whether we are currently importing multiple copies
+ 'urlArgs', // whether to add args into url tokens
+ 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
+ 'pluginManager', // Used as the plugin manager for the session
+ 'importantScope' // used to bubble up !important statements
+ ];
+
+contexts.Eval = function(options, frames) {
+ copyFromOriginal(options, this, evalCopyProperties);
+
+ this.frames = frames || [];
+ this.importantScope = this.importantScope || [];
+};
+
+contexts.Eval.prototype.inParenthesis = function () {
+ if (!this.parensStack) {
+ this.parensStack = [];
+ }
+ this.parensStack.push(true);
+};
+
+contexts.Eval.prototype.outOfParenthesis = function () {
+ this.parensStack.pop();
+};
+
+contexts.Eval.prototype.isMathOn = function () {
+ return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
+};
+
+contexts.Eval.prototype.isPathRelative = function (path) {
+ return !/^(?:[a-z-]+:|\/|#)/i.test(path);
+};
+
+contexts.Eval.prototype.normalizePath = function( path ) {
+ var
+ segments = path.split("/").reverse(),
+ segment;
+
+ path = [];
+ while (segments.length !== 0 ) {
+ segment = segments.pop();
+ switch( segment ) {
+ case ".":
+ break;
+ case "..":
+ if ((path.length === 0) || (path[path.length - 1] === "..")) {
+ path.push( segment );
+ } else {
+ path.pop();
+ }
+ break;
+ default:
+ path.push( segment );
+ break;
+ }
+ }
+
+ return path.join("/");
+};
+
+//todo - do the same for the toCSS ?
--- /dev/null
+module.exports = {
+ 'aliceblue':'#f0f8ff',
+ 'antiquewhite':'#faebd7',
+ 'aqua':'#00ffff',
+ 'aquamarine':'#7fffd4',
+ 'azure':'#f0ffff',
+ 'beige':'#f5f5dc',
+ 'bisque':'#ffe4c4',
+ 'black':'#000000',
+ 'blanchedalmond':'#ffebcd',
+ 'blue':'#0000ff',
+ 'blueviolet':'#8a2be2',
+ 'brown':'#a52a2a',
+ 'burlywood':'#deb887',
+ 'cadetblue':'#5f9ea0',
+ 'chartreuse':'#7fff00',
+ 'chocolate':'#d2691e',
+ 'coral':'#ff7f50',
+ 'cornflowerblue':'#6495ed',
+ 'cornsilk':'#fff8dc',
+ 'crimson':'#dc143c',
+ 'cyan':'#00ffff',
+ 'darkblue':'#00008b',
+ 'darkcyan':'#008b8b',
+ 'darkgoldenrod':'#b8860b',
+ 'darkgray':'#a9a9a9',
+ 'darkgrey':'#a9a9a9',
+ 'darkgreen':'#006400',
+ 'darkkhaki':'#bdb76b',
+ 'darkmagenta':'#8b008b',
+ 'darkolivegreen':'#556b2f',
+ 'darkorange':'#ff8c00',
+ 'darkorchid':'#9932cc',
+ 'darkred':'#8b0000',
+ 'darksalmon':'#e9967a',
+ 'darkseagreen':'#8fbc8f',
+ 'darkslateblue':'#483d8b',
+ 'darkslategray':'#2f4f4f',
+ 'darkslategrey':'#2f4f4f',
+ 'darkturquoise':'#00ced1',
+ 'darkviolet':'#9400d3',
+ 'deeppink':'#ff1493',
+ 'deepskyblue':'#00bfff',
+ 'dimgray':'#696969',
+ 'dimgrey':'#696969',
+ 'dodgerblue':'#1e90ff',
+ 'firebrick':'#b22222',
+ 'floralwhite':'#fffaf0',
+ 'forestgreen':'#228b22',
+ 'fuchsia':'#ff00ff',
+ 'gainsboro':'#dcdcdc',
+ 'ghostwhite':'#f8f8ff',
+ 'gold':'#ffd700',
+ 'goldenrod':'#daa520',
+ 'gray':'#808080',
+ 'grey':'#808080',
+ 'green':'#008000',
+ 'greenyellow':'#adff2f',
+ 'honeydew':'#f0fff0',
+ 'hotpink':'#ff69b4',
+ 'indianred':'#cd5c5c',
+ 'indigo':'#4b0082',
+ 'ivory':'#fffff0',
+ 'khaki':'#f0e68c',
+ 'lavender':'#e6e6fa',
+ 'lavenderblush':'#fff0f5',
+ 'lawngreen':'#7cfc00',
+ 'lemonchiffon':'#fffacd',
+ 'lightblue':'#add8e6',
+ 'lightcoral':'#f08080',
+ 'lightcyan':'#e0ffff',
+ 'lightgoldenrodyellow':'#fafad2',
+ 'lightgray':'#d3d3d3',
+ 'lightgrey':'#d3d3d3',
+ 'lightgreen':'#90ee90',
+ 'lightpink':'#ffb6c1',
+ 'lightsalmon':'#ffa07a',
+ 'lightseagreen':'#20b2aa',
+ 'lightskyblue':'#87cefa',
+ 'lightslategray':'#778899',
+ 'lightslategrey':'#778899',
+ 'lightsteelblue':'#b0c4de',
+ 'lightyellow':'#ffffe0',
+ 'lime':'#00ff00',
+ 'limegreen':'#32cd32',
+ 'linen':'#faf0e6',
+ 'magenta':'#ff00ff',
+ 'maroon':'#800000',
+ 'mediumaquamarine':'#66cdaa',
+ 'mediumblue':'#0000cd',
+ 'mediumorchid':'#ba55d3',
+ 'mediumpurple':'#9370d8',
+ 'mediumseagreen':'#3cb371',
+ 'mediumslateblue':'#7b68ee',
+ 'mediumspringgreen':'#00fa9a',
+ 'mediumturquoise':'#48d1cc',
+ 'mediumvioletred':'#c71585',
+ 'midnightblue':'#191970',
+ 'mintcream':'#f5fffa',
+ 'mistyrose':'#ffe4e1',
+ 'moccasin':'#ffe4b5',
+ 'navajowhite':'#ffdead',
+ 'navy':'#000080',
+ 'oldlace':'#fdf5e6',
+ 'olive':'#808000',
+ 'olivedrab':'#6b8e23',
+ 'orange':'#ffa500',
+ 'orangered':'#ff4500',
+ 'orchid':'#da70d6',
+ 'palegoldenrod':'#eee8aa',
+ 'palegreen':'#98fb98',
+ 'paleturquoise':'#afeeee',
+ 'palevioletred':'#d87093',
+ 'papayawhip':'#ffefd5',
+ 'peachpuff':'#ffdab9',
+ 'peru':'#cd853f',
+ 'pink':'#ffc0cb',
+ 'plum':'#dda0dd',
+ 'powderblue':'#b0e0e6',
+ 'purple':'#800080',
+ 'rebeccapurple':'#663399',
+ 'red':'#ff0000',
+ 'rosybrown':'#bc8f8f',
+ 'royalblue':'#4169e1',
+ 'saddlebrown':'#8b4513',
+ 'salmon':'#fa8072',
+ 'sandybrown':'#f4a460',
+ 'seagreen':'#2e8b57',
+ 'seashell':'#fff5ee',
+ 'sienna':'#a0522d',
+ 'silver':'#c0c0c0',
+ 'skyblue':'#87ceeb',
+ 'slateblue':'#6a5acd',
+ 'slategray':'#708090',
+ 'slategrey':'#708090',
+ 'snow':'#fffafa',
+ 'springgreen':'#00ff7f',
+ 'steelblue':'#4682b4',
+ 'tan':'#d2b48c',
+ 'teal':'#008080',
+ 'thistle':'#d8bfd8',
+ 'tomato':'#ff6347',
+ 'turquoise':'#40e0d0',
+ 'violet':'#ee82ee',
+ 'wheat':'#f5deb3',
+ 'white':'#ffffff',
+ 'whitesmoke':'#f5f5f5',
+ 'yellow':'#ffff00',
+ 'yellowgreen':'#9acd32'
+};
\ No newline at end of file
--- /dev/null
+module.exports = {
+ colors: require("./colors"),
+ unitConversions: require("./unit-conversions")
+};
--- /dev/null
+module.exports = {
+ length: {
+ 'm': 1,
+ 'cm': 0.01,
+ 'mm': 0.001,
+ 'in': 0.0254,
+ 'px': 0.0254 / 96,
+ 'pt': 0.0254 / 72,
+ 'pc': 0.0254 / 72 * 12
+ },
+ duration: {
+ 's': 1,
+ 'ms': 0.001
+ },
+ angle: {
+ 'rad': 1 / (2 * Math.PI),
+ 'deg': 1 / 360,
+ 'grad': 1 / 400,
+ 'turn': 1
+ }
+};
\ No newline at end of file
--- /dev/null
+var abstractFileManager = function() {
+};
+
+abstractFileManager.prototype.getPath = function (filename) {
+ var j = filename.lastIndexOf('?');
+ if (j > 0) {
+ filename = filename.slice(0, j);
+ }
+ j = filename.lastIndexOf('/');
+ if (j < 0) {
+ j = filename.lastIndexOf('\\');
+ }
+ if (j < 0) {
+ return "";
+ }
+ return filename.slice(0, j + 1);
+};
+
+abstractFileManager.prototype.tryAppendLessExtension = function(path) {
+ return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + '.less';
+};
+
+abstractFileManager.prototype.supportsSync = function() {
+ return false;
+};
+
+abstractFileManager.prototype.alwaysMakePathsAbsolute = function() {
+ return false;
+};
+
+abstractFileManager.prototype.isPathAbsolute = function(filename) {
+ return (/^(?:[a-z-]+:|\/|\\|#)/i).test(filename);
+};
+
+abstractFileManager.prototype.join = function(basePath, laterPath) {
+ if (!basePath) {
+ return laterPath;
+ }
+ return basePath + laterPath;
+};
+abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
+ // diff between two paths to create a relative path
+
+ var urlParts = this.extractUrlParts(url),
+ baseUrlParts = this.extractUrlParts(baseUrl),
+ i, max, urlDirectories, baseUrlDirectories, diff = "";
+ if (urlParts.hostPart !== baseUrlParts.hostPart) {
+ return "";
+ }
+ max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
+ for (i = 0; i < max; i++) {
+ if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
+ }
+ baseUrlDirectories = baseUrlParts.directories.slice(i);
+ urlDirectories = urlParts.directories.slice(i);
+ for (i = 0; i < baseUrlDirectories.length - 1; i++) {
+ diff += "../";
+ }
+ for (i = 0; i < urlDirectories.length - 1; i++) {
+ diff += urlDirectories[i] + "/";
+ }
+ return diff;
+};
+// helper function, not part of API
+abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) {
+ // urlParts[1] = protocol&hostname || /
+ // urlParts[2] = / if path relative to host base
+ // urlParts[3] = directories
+ // urlParts[4] = filename
+ // urlParts[5] = parameters
+
+ var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
+ urlParts = url.match(urlPartsRegex),
+ returner = {}, directories = [], i, baseUrlParts;
+
+ if (!urlParts) {
+ throw new Error("Could not parse sheet href - '" + url + "'");
+ }
+
+ // Stylesheets in IE don't always return the full path
+ if (baseUrl && (!urlParts[1] || urlParts[2])) {
+ baseUrlParts = baseUrl.match(urlPartsRegex);
+ if (!baseUrlParts) {
+ throw new Error("Could not parse page url - '" + baseUrl + "'");
+ }
+ urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
+ if (!urlParts[2]) {
+ urlParts[3] = baseUrlParts[3] + urlParts[3];
+ }
+ }
+
+ if (urlParts[3]) {
+ directories = urlParts[3].replace(/\\/g, "/").split("/");
+
+ // extract out . before .. so .. doesn't absorb a non-directory
+ for (i = 0; i < directories.length; i++) {
+ if (directories[i] === ".") {
+ directories.splice(i, 1);
+ i -= 1;
+ }
+ }
+
+ for (i = 0; i < directories.length; i++) {
+ if (directories[i] === ".." && i > 0) {
+ directories.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+
+ returner.hostPart = urlParts[1];
+ returner.directories = directories;
+ returner.path = (urlParts[1] || "") + directories.join("/");
+ returner.fileUrl = returner.path + (urlParts[4] || "");
+ returner.url = returner.fileUrl + (urlParts[5] || "");
+ return returner;
+};
+
+module.exports = abstractFileManager;
--- /dev/null
+module.exports = {
+ /**
+ * Converts a string to a base 64 string
+ * @param str
+ */
+ encodeBase64: function(str) {
+ },
+ /**
+ * Lookup the mime-type of a filename
+ * @param filename
+ */
+ mimeLookup: function (filename) {
+ },
+ /**
+ * Look up the charset of a mime type
+ * @param mime
+ */
+ charsetLookup: function (mime) {
+ },
+ /**
+ * Gets a source map generator
+ */
+ getSourceMapGenerator: function getSourceMapGenerator() {
+ }
+};
--- /dev/null
+var logger = require("../logger");
+var environment = function(externalEnvironment, fileManagers) {
+ this.fileManagers = fileManagers || [];
+ externalEnvironment = externalEnvironment || {};
+
+ var optionalFunctions = ["encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"],
+ requiredFunctions = [],
+ functions = requiredFunctions.concat(optionalFunctions);
+
+ for (var i = 0; i < functions.length; i++) {
+ var propName = functions[i],
+ environmentFunc = externalEnvironment[propName];
+ if (environmentFunc) {
+ this[propName] = environmentFunc.bind(externalEnvironment);
+ } else if (i < requiredFunctions.length) {
+ this.warn("missing required function in environment - " + propName);
+ }
+ }
+};
+
+environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) {
+
+ if (!filename) {
+ logger.warn("getFileManager called with no filename.. Please report this issue. continuing.");
+ }
+ if (currentDirectory == null) {
+ logger.warn("getFileManager called with null directory.. Please report this issue. continuing.");
+ }
+
+ var fileManagers = this.fileManagers;
+ if (options.pluginManager) {
+ fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers());
+ }
+ for (var i = fileManagers.length - 1; i >= 0 ; i--) {
+ var fileManager = fileManagers[i];
+ if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) {
+ return fileManager;
+ }
+ }
+ return null;
+};
+
+environment.prototype.addFileManager = function (fileManager) {
+ this.fileManagers.push(fileManager);
+};
+
+environment.prototype.clearFileManagers = function () {
+ this.fileManagers = [];
+};
+
+module.exports = environment;
--- /dev/null
+module.exports = {
+ /**
+ * Given the full path to a file, return the path component
+ * Provided by AbstractFileManager
+ * @param {string} filename
+ * @returns {string}
+ */
+ getPath: function(filename) {
+ },
+ /**
+ * Append a .less extension if appropriate. Only called if less thinks one could be added.
+ * Provided by AbstractFileManager
+ * @param filename
+ * @returns {string}
+ */
+ tryAppendLessExtension: function(filename) {
+ },
+ /**
+ * Whether the rootpath should be converted to be absolute.
+ * The browser ovverides this to return true because urls must be absolute.
+ * Provided by AbstractFileManager (returns false)
+ * @returns {bool}
+ */
+ alwaysMakePathsAbsolute: function() {
+ },
+ /**
+ * Returns whether a path is absolute
+ * Provided by AbstractFileManager
+ * @param {string} path
+ * @returns {bool}
+ */
+ isPathAbsolute: function(path) {
+ },
+ /**
+ * joins together 2 paths
+ * Provided by AbstractFileManager
+ * @param {string} basePath
+ * @param {string} laterPath
+ */
+ join: function(basePath, laterPath) {
+ },
+ /**
+ * Returns the difference between 2 paths
+ * E.g. url = a/ baseUrl = a/b/ returns ../
+ * url = a/b/ baseUrl = a/ returns b/
+ * Provided by AbstractFileManager
+ * @param {string} url
+ * @param {string} baseUrl
+ * @returns {string}
+ */
+ pathDiff: function(url, baseUrl) {
+ },
+ /**
+ * Returns whether this file manager supports this file for syncronous file retrieval
+ * If true is returned, loadFileSync will then be called with the file.
+ * Provided by AbstractFileManager (returns false)
+ * @param {string} filename
+ * @param {string} currentDirectory
+ * @param {object} options
+ * @param {less.environment.environment} environment
+ * @returns {bool}
+ */
+ supportsSync: function(filename, currentDirectory, options, environment) {
+ },
+ /**
+ *
+ * @param {string} filename
+ * @param {string} currentDirectory
+ * @param {object} options
+ * @param {less.environment.environment} environment
+ * @returns {bool}
+ */
+ supports: function(filename, currentDirectory, options, environment) {
+ },
+ /**
+ * Loads a file asynchronously. Expects a promise that either rejects with an error or fulfills with an
+ * object containing
+ * { filename: - full resolved path to file
+ * contents: - the contents of the file, as a string }
+ *
+ * @param {string} filename
+ * @param {string} currentDirectory
+ * @param {object} options
+ * @param {less.environment.environment} environment
+ * @returns {Promise}
+ */
+ loadFile: function(filename, currentDirectory, options, environment) {
+ },
+ /**
+ * Loads a file synchronously. Expects an immediate return with an object containing
+ * { error: - error object if an error occurs
+ * filename: - full resolved path to file
+ * contents: - the contents of the file, as a string }
+ *
+ * @param {string} filename
+ * @param {string} currentDirectory
+ * @param {object} options
+ * @param {less.environment.environment} environment
+ * @returns {object} should be an object containing error or contents and filename
+ */
+ loadFileSync: function(filename, currentDirectory, options, environment) {
+ }
+};
--- /dev/null
+var Color = require("../tree/color"),
+ functionRegistry = require("./function-registry");
+
+// Color Blending
+// ref: http://www.w3.org/TR/compositing-1
+
+function colorBlend(mode, color1, color2) {
+ var ab = color1.alpha, cb, // backdrop
+ as = color2.alpha, cs, // source
+ ar, cr, r = []; // result
+
+ ar = as + ab * (1 - as);
+ for (var i = 0; i < 3; i++) {
+ cb = color1.rgb[i] / 255;
+ cs = color2.rgb[i] / 255;
+ cr = mode(cb, cs);
+ if (ar) {
+ cr = (as * cs + ab * (cb -
+ as * (cb + cs - cr))) / ar;
+ }
+ r[i] = cr * 255;
+ }
+
+ return new Color(r, ar);
+}
+
+var colorBlendModeFunctions = {
+ multiply: function(cb, cs) {
+ return cb * cs;
+ },
+ screen: function(cb, cs) {
+ return cb + cs - cb * cs;
+ },
+ overlay: function(cb, cs) {
+ cb *= 2;
+ return (cb <= 1) ?
+ colorBlendModeFunctions.multiply(cb, cs) :
+ colorBlendModeFunctions.screen(cb - 1, cs);
+ },
+ softlight: function(cb, cs) {
+ var d = 1, e = cb;
+ if (cs > 0.5) {
+ e = 1;
+ d = (cb > 0.25) ? Math.sqrt(cb)
+ : ((16 * cb - 12) * cb + 4) * cb;
+ }
+ return cb - (1 - 2 * cs) * e * (d - cb);
+ },
+ hardlight: function(cb, cs) {
+ return colorBlendModeFunctions.overlay(cs, cb);
+ },
+ difference: function(cb, cs) {
+ return Math.abs(cb - cs);
+ },
+ exclusion: function(cb, cs) {
+ return cb + cs - 2 * cb * cs;
+ },
+
+ // non-w3c functions:
+ average: function(cb, cs) {
+ return (cb + cs) / 2;
+ },
+ negation: function(cb, cs) {
+ return 1 - Math.abs(cb + cs - 1);
+ }
+};
+
+for (var f in colorBlendModeFunctions) {
+ if (colorBlendModeFunctions.hasOwnProperty(f)) {
+ colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]);
+ }
+}
+
+functionRegistry.addMultiple(colorBlend);
--- /dev/null
+var Dimension = require("../tree/dimension"),
+ Color = require("../tree/color"),
+ Quoted = require("../tree/quoted"),
+ Anonymous = require("../tree/anonymous"),
+ functionRegistry = require("./function-registry"),
+ colorFunctions;
+
+function clamp(val) {
+ return Math.min(1, Math.max(0, val));
+}
+function hsla(color) {
+ return colorFunctions.hsla(color.h, color.s, color.l, color.a);
+}
+function number(n) {
+ if (n instanceof Dimension) {
+ return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
+ } else if (typeof n === 'number') {
+ return n;
+ } else {
+ throw {
+ type: "Argument",
+ message: "color functions take numbers as parameters"
+ };
+ }
+}
+function scaled(n, size) {
+ if (n instanceof Dimension && n.unit.is('%')) {
+ return parseFloat(n.value * size / 100);
+ } else {
+ return number(n);
+ }
+}
+colorFunctions = {
+ rgb: function (r, g, b) {
+ return colorFunctions.rgba(r, g, b, 1.0);
+ },
+ rgba: function (r, g, b, a) {
+ var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
+ a = number(a);
+ return new Color(rgb, a);
+ },
+ hsl: function (h, s, l) {
+ return colorFunctions.hsla(h, s, l, 1.0);
+ },
+ hsla: function (h, s, l, a) {
+ function hue(h) {
+ h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
+ if (h * 6 < 1) {
+ return m1 + (m2 - m1) * h * 6;
+ }
+ else if (h * 2 < 1) {
+ return m2;
+ }
+ else if (h * 3 < 2) {
+ return m1 + (m2 - m1) * (2 / 3 - h) * 6;
+ }
+ else {
+ return m1;
+ }
+ }
+
+ h = (number(h) % 360) / 360;
+ s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
+
+ var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
+ var m1 = l * 2 - m2;
+
+ return colorFunctions.rgba(hue(h + 1 / 3) * 255,
+ hue(h) * 255,
+ hue(h - 1 / 3) * 255,
+ a);
+ },
+
+ hsv: function(h, s, v) {
+ return colorFunctions.hsva(h, s, v, 1.0);
+ },
+
+ hsva: function(h, s, v, a) {
+ h = ((number(h) % 360) / 360) * 360;
+ s = number(s); v = number(v); a = number(a);
+
+ var i, f;
+ i = Math.floor((h / 60) % 6);
+ f = (h / 60) - i;
+
+ var vs = [v,
+ v * (1 - s),
+ v * (1 - f * s),
+ v * (1 - (1 - f) * s)];
+ var perm = [[0, 3, 1],
+ [2, 0, 1],
+ [1, 0, 3],
+ [1, 2, 0],
+ [3, 1, 0],
+ [0, 1, 2]];
+
+ return colorFunctions.rgba(vs[perm[i][0]] * 255,
+ vs[perm[i][1]] * 255,
+ vs[perm[i][2]] * 255,
+ a);
+ },
+
+ hue: function (color) {
+ return new Dimension(color.toHSL().h);
+ },
+ saturation: function (color) {
+ return new Dimension(color.toHSL().s * 100, '%');
+ },
+ lightness: function (color) {
+ return new Dimension(color.toHSL().l * 100, '%');
+ },
+ hsvhue: function(color) {
+ return new Dimension(color.toHSV().h);
+ },
+ hsvsaturation: function (color) {
+ return new Dimension(color.toHSV().s * 100, '%');
+ },
+ hsvvalue: function (color) {
+ return new Dimension(color.toHSV().v * 100, '%');
+ },
+ red: function (color) {
+ return new Dimension(color.rgb[0]);
+ },
+ green: function (color) {
+ return new Dimension(color.rgb[1]);
+ },
+ blue: function (color) {
+ return new Dimension(color.rgb[2]);
+ },
+ alpha: function (color) {
+ return new Dimension(color.toHSL().a);
+ },
+ luma: function (color) {
+ return new Dimension(color.luma() * color.alpha * 100, '%');
+ },
+ luminance: function (color) {
+ var luminance =
+ (0.2126 * color.rgb[0] / 255) +
+ (0.7152 * color.rgb[1] / 255) +
+ (0.0722 * color.rgb[2] / 255);
+
+ return new Dimension(luminance * color.alpha * 100, '%');
+ },
+ saturate: function (color, amount) {
+ // filter: saturate(3.2);
+ // should be kept as is, so check for color
+ if (!color.rgb) {
+ return null;
+ }
+ var hsl = color.toHSL();
+
+ hsl.s += amount.value / 100;
+ hsl.s = clamp(hsl.s);
+ return hsla(hsl);
+ },
+ desaturate: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.s -= amount.value / 100;
+ hsl.s = clamp(hsl.s);
+ return hsla(hsl);
+ },
+ lighten: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.l += amount.value / 100;
+ hsl.l = clamp(hsl.l);
+ return hsla(hsl);
+ },
+ darken: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.l -= amount.value / 100;
+ hsl.l = clamp(hsl.l);
+ return hsla(hsl);
+ },
+ fadein: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.a += amount.value / 100;
+ hsl.a = clamp(hsl.a);
+ return hsla(hsl);
+ },
+ fadeout: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.a -= amount.value / 100;
+ hsl.a = clamp(hsl.a);
+ return hsla(hsl);
+ },
+ fade: function (color, amount) {
+ var hsl = color.toHSL();
+
+ hsl.a = amount.value / 100;
+ hsl.a = clamp(hsl.a);
+ return hsla(hsl);
+ },
+ spin: function (color, amount) {
+ var hsl = color.toHSL();
+ var hue = (hsl.h + amount.value) % 360;
+
+ hsl.h = hue < 0 ? 360 + hue : hue;
+
+ return hsla(hsl);
+ },
+ //
+ // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
+ // http://sass-lang.com
+ //
+ mix: function (color1, color2, weight) {
+ if (!weight) {
+ weight = new Dimension(50);
+ }
+ var p = weight.value / 100.0;
+ var w = p * 2 - 1;
+ var a = color1.toHSL().a - color2.toHSL().a;
+
+ var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
+ var w2 = 1 - w1;
+
+ var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
+ color1.rgb[1] * w1 + color2.rgb[1] * w2,
+ color1.rgb[2] * w1 + color2.rgb[2] * w2];
+
+ var alpha = color1.alpha * p + color2.alpha * (1 - p);
+
+ return new Color(rgb, alpha);
+ },
+ greyscale: function (color) {
+ return colorFunctions.desaturate(color, new Dimension(100));
+ },
+ contrast: function (color, dark, light, threshold) {
+ // filter: contrast(3.2);
+ // should be kept as is, so check for color
+ if (!color.rgb) {
+ return null;
+ }
+ if (typeof light === 'undefined') {
+ light = colorFunctions.rgba(255, 255, 255, 1.0);
+ }
+ if (typeof dark === 'undefined') {
+ dark = colorFunctions.rgba(0, 0, 0, 1.0);
+ }
+ //Figure out which is actually light and dark!
+ if (dark.luma() > light.luma()) {
+ var t = light;
+ light = dark;
+ dark = t;
+ }
+ if (typeof threshold === 'undefined') {
+ threshold = 0.43;
+ } else {
+ threshold = number(threshold);
+ }
+ if (color.luma() < threshold) {
+ return light;
+ } else {
+ return dark;
+ }
+ },
+ argb: function (color) {
+ return new Anonymous(color.toARGB());
+ },
+ color: function(c) {
+ if ((c instanceof Quoted) &&
+ (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
+ return new Color(c.value.slice(1));
+ }
+ if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
+ c.value = undefined;
+ return c;
+ }
+ throw {
+ type: "Argument",
+ message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF"
+ };
+ },
+ tint: function(color, amount) {
+ return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
+ },
+ shade: function(color, amount) {
+ return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
+ }
+};
+functionRegistry.addMultiple(colorFunctions);
--- /dev/null
+module.exports = function(environment) {
+ var Quoted = require("../tree/quoted"),
+ URL = require("../tree/url"),
+ functionRegistry = require("./function-registry"),
+ fallback = function(functionThis, node) {
+ return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context);
+ },
+ logger = require('../logger');
+
+ functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) {
+
+ if (!filePathNode) {
+ filePathNode = mimetypeNode;
+ mimetypeNode = null;
+ }
+
+ var mimetype = mimetypeNode && mimetypeNode.value;
+ var filePath = filePathNode.value;
+ var currentFileInfo = this.currentFileInfo;
+ var currentDirectory = currentFileInfo.relativeUrls ?
+ currentFileInfo.currentDirectory : currentFileInfo.entryPath;
+
+ var fragmentStart = filePath.indexOf('#');
+ var fragment = '';
+ if (fragmentStart !== -1) {
+ fragment = filePath.slice(fragmentStart);
+ filePath = filePath.slice(0, fragmentStart);
+ }
+
+ var fileManager = environment.getFileManager(filePath, currentDirectory, this.context, environment, true);
+
+ if (!fileManager) {
+ return fallback(this, filePathNode);
+ }
+
+ var useBase64 = false;
+
+ // detect the mimetype if not given
+ if (!mimetypeNode) {
+
+ mimetype = environment.mimeLookup(filePath);
+
+ if (mimetype === "image/svg+xml") {
+ useBase64 = false;
+ } else {
+ // use base 64 unless it's an ASCII or UTF-8 format
+ var charset = environment.charsetLookup(mimetype);
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
+ }
+ if (useBase64) { mimetype += ';base64'; }
+ }
+ else {
+ useBase64 = /;base64$/.test(mimetype);
+ }
+
+ var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
+ if (!fileSync.contents) {
+ logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
+ return fallback(this, filePathNode || mimetypeNode);
+ }
+ var buf = fileSync.contents;
+ if (useBase64 && !environment.encodeBase64) {
+ return fallback(this, filePathNode);
+ }
+
+ buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
+
+ var uri = "data:" + mimetype + ',' + buf + fragment;
+
+ // IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
+ // and the --ieCompat flag is enabled, return a normal url() instead.
+ var DATA_URI_MAX = 32768;
+ if (uri.length >= DATA_URI_MAX) {
+
+ if (this.context.ieCompat !== false) {
+ logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length +
+ " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
+
+ return fallback(this, filePathNode || mimetypeNode);
+ }
+ }
+
+ return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
+ });
+};
--- /dev/null
+var Keyword = require("../tree/keyword"),
+ functionRegistry = require("./function-registry");
+
+var defaultFunc = {
+ eval: function () {
+ var v = this.value_, e = this.error_;
+ if (e) {
+ throw e;
+ }
+ if (v != null) {
+ return v ? Keyword.True : Keyword.False;
+ }
+ },
+ value: function (v) {
+ this.value_ = v;
+ },
+ error: function (e) {
+ this.error_ = e;
+ },
+ reset: function () {
+ this.value_ = this.error_ = null;
+ }
+};
+
+functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
+
+module.exports = defaultFunc;
--- /dev/null
+var functionRegistry = require("./function-registry");
+
+var functionCaller = function(name, context, index, currentFileInfo) {
+ this.name = name.toLowerCase();
+ this.func = functionRegistry.get(this.name);
+ this.index = index;
+ this.context = context;
+ this.currentFileInfo = currentFileInfo;
+};
+functionCaller.prototype.isValid = function() {
+ return Boolean(this.func);
+};
+functionCaller.prototype.call = function(args) {
+ return this.func.apply(this, args);
+};
+
+module.exports = functionCaller;
--- /dev/null
+module.exports = {
+ _data: {},
+ add: function(name, func) {
+ if (this._data.hasOwnProperty(name)) {
+ //TODO warn
+ }
+ this._data[name] = func;
+ },
+ addMultiple: function(functions) {
+ Object.keys(functions).forEach(
+ function(name) {
+ this.add(name, functions[name]);
+ }.bind(this));
+ },
+ get: function(name) {
+ return this._data[name];
+ }
+};
--- /dev/null
+module.exports = function(environment) {
+ var functions = {
+ functionRegistry: require("./function-registry"),
+ functionCaller: require("./function-caller")
+ };
+
+ //register functions
+ require("./default");
+ require("./color");
+ require("./color-blending");
+ require("./data-uri")(environment);
+ require("./math");
+ require("./number");
+ require("./string");
+ require("./svg")(environment);
+ require("./types");
+
+ return functions;
+};
--- /dev/null
+var Dimension = require("../tree/dimension"),
+ functionRegistry = require("./function-registry");
+
+var mathFunctions = {
+ // name, unit
+ ceil: null,
+ floor: null,
+ sqrt: null,
+ abs: null,
+ tan: "",
+ sin: "",
+ cos: "",
+ atan: "rad",
+ asin: "rad",
+ acos: "rad"
+};
+
+function _math(fn, unit, n) {
+ if (!(n instanceof Dimension)) {
+ throw { type: "Argument", message: "argument must be a number" };
+ }
+ if (unit == null) {
+ unit = n.unit;
+ } else {
+ n = n.unify();
+ }
+ return new Dimension(fn(parseFloat(n.value)), unit);
+}
+
+for (var f in mathFunctions) {
+ if (mathFunctions.hasOwnProperty(f)) {
+ mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]);
+ }
+}
+
+mathFunctions.round = function (n, f) {
+ var fraction = typeof f === "undefined" ? 0 : f.value;
+ return _math(function(num) { return num.toFixed(fraction); }, null, n);
+};
+
+functionRegistry.addMultiple(mathFunctions);
--- /dev/null
+var Dimension = require("../tree/dimension"),
+ Anonymous = require("../tree/anonymous"),
+ functionRegistry = require("./function-registry");
+
+var minMax = function (isMin, args) {
+ args = Array.prototype.slice.call(args);
+ switch(args.length) {
+ case 0: throw { type: "Argument", message: "one or more arguments required" };
+ }
+ var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
+ order = [], // elems only contains original argument values.
+ values = {}; // key is the unit.toString() for unified Dimension values,
+ // value is the index into the order array.
+ for (i = 0; i < args.length; i++) {
+ current = args[i];
+ if (!(current instanceof Dimension)) {
+ if (Array.isArray(args[i].value)) {
+ Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
+ }
+ continue;
+ }
+ currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify();
+ unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
+ unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic;
+ unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
+ j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
+ if (j === undefined) {
+ if (unitStatic !== undefined && unit !== unitStatic) {
+ throw{ type: "Argument", message: "incompatible types" };
+ }
+ values[unit] = order.length;
+ order.push(current);
+ continue;
+ }
+ referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify();
+ if ( isMin && currentUnified.value < referenceUnified.value ||
+ !isMin && currentUnified.value > referenceUnified.value) {
+ order[j] = current;
+ }
+ }
+ if (order.length == 1) {
+ return order[0];
+ }
+ args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? "," : ", ");
+ return new Anonymous((isMin ? "min" : "max") + "(" + args + ")");
+};
+functionRegistry.addMultiple({
+ min: function () {
+ return minMax(true, arguments);
+ },
+ max: function () {
+ return minMax(false, arguments);
+ },
+ convert: function (val, unit) {
+ return val.convertTo(unit.value);
+ },
+ pi: function () {
+ return new Dimension(Math.PI);
+ },
+ mod: function(a, b) {
+ return new Dimension(a.value % b.value, a.unit);
+ },
+ pow: function(x, y) {
+ if (typeof x === "number" && typeof y === "number") {
+ x = new Dimension(x);
+ y = new Dimension(y);
+ } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) {
+ throw { type: "Argument", message: "arguments must be numbers" };
+ }
+
+ return new Dimension(Math.pow(x.value, y.value), x.unit);
+ },
+ percentage: function (n) {
+ return new Dimension(n.value * 100, '%');
+ }
+});
--- /dev/null
+var Quoted = require("../tree/quoted"),
+ Anonymous = require("../tree/anonymous"),
+ JavaScript = require("../tree/javascript"),
+ functionRegistry = require("./function-registry");
+
+functionRegistry.addMultiple({
+ e: function (str) {
+ return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value);
+ },
+ escape: function (str) {
+ return new Anonymous(
+ encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B")
+ .replace(/\(/g, "%28").replace(/\)/g, "%29"));
+ },
+ replace: function (string, pattern, replacement, flags) {
+ var result = string.value;
+
+ result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value);
+ return new Quoted(string.quote || '', result, string.escaped);
+ },
+ '%': function (string /* arg, arg, ...*/) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ result = string.value;
+
+ for (var i = 0; i < args.length; i++) {
+ /*jshint loopfunc:true */
+ result = result.replace(/%[sda]/i, function(token) {
+ var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
+ return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
+ });
+ }
+ result = result.replace(/%%/g, '%');
+ return new Quoted(string.quote || '', result, string.escaped);
+ }
+});
--- /dev/null
+module.exports = function(environment) {
+ var Dimension = require("../tree/dimension"),
+ Color = require("../tree/color"),
+ Expression = require("../tree/expression"),
+ Quoted = require("../tree/quoted"),
+ URL = require("../tree/url"),
+ functionRegistry = require("./function-registry");
+
+ functionRegistry.add("svg-gradient", function(direction) {
+
+ function throwArgumentDescriptor() {
+ throw { type: "Argument",
+ message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
+ " end_color [end_position]" };
+ }
+
+ if (arguments.length < 3) {
+ throwArgumentDescriptor();
+ }
+ var stops = Array.prototype.slice.call(arguments, 1),
+ gradientDirectionSvg,
+ gradientType = "linear",
+ rectangleDimension = 'x="0" y="0" width="1" height="1"',
+ renderEnv = {compress: false},
+ returner,
+ directionValue = direction.toCSS(renderEnv),
+ i, color, position, positionValue, alpha;
+
+ switch (directionValue) {
+ case "to bottom":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
+ break;
+ case "to right":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
+ break;
+ case "to bottom right":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
+ break;
+ case "to top right":
+ gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
+ break;
+ case "ellipse":
+ case "ellipse at center":
+ gradientType = "radial";
+ gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
+ rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
+ break;
+ default:
+ throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right'," +
+ " 'to bottom right', 'to top right' or 'ellipse at center'" };
+ }
+ returner = '<?xml version="1.0" ?>' +
+ '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
+ '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
+
+ for (i = 0; i < stops.length; i+= 1) {
+ if (stops[i] instanceof Expression) {
+ color = stops[i].value[0];
+ position = stops[i].value[1];
+ } else {
+ color = stops[i];
+ position = undefined;
+ }
+
+ if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
+ throwArgumentDescriptor();
+ }
+ positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
+ alpha = color.alpha;
+ returner += '<stop offset="' + positionValue + '" stop-color="' + color.toRGB() + '"' + (alpha < 1 ? ' stop-opacity="' + alpha + '"' : '') + '/>';
+ }
+ returner += '</' + gradientType + 'Gradient>' +
+ '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
+
+ returner = encodeURIComponent(returner);
+
+ returner = "data:image/svg+xml," + returner;
+ return new URL(new Quoted("'" + returner + "'", returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
+ });
+};
--- /dev/null
+var Keyword = require("../tree/keyword"),
+ DetachedRuleset = require("../tree/detached-ruleset"),
+ Dimension = require("../tree/dimension"),
+ Color = require("../tree/color"),
+ Quoted = require("../tree/quoted"),
+ Anonymous = require("../tree/anonymous"),
+ URL = require("../tree/url"),
+ Operation = require("../tree/operation"),
+ functionRegistry = require("./function-registry");
+
+var isa = function (n, Type) {
+ return (n instanceof Type) ? Keyword.True : Keyword.False;
+ },
+ isunit = function (n, unit) {
+ if (unit === undefined) {
+ throw { type: "Argument", message: "missing the required second argument to isunit." };
+ }
+ unit = typeof unit.value === "string" ? unit.value : unit;
+ if (typeof unit !== "string") {
+ throw { type: "Argument", message: "Second argument to isunit should be a unit or a string." };
+ }
+ return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
+ },
+ getItemsFromNode = function(node) {
+ // handle non-array values as an array of length 1
+ // return 'undefined' if index is invalid
+ var items = Array.isArray(node.value) ?
+ node.value : Array(node);
+
+ return items.filter(function(item) {
+ if (item.type === "Comment") {
+ return false;
+ }
+ return true;
+ });
+ };
+functionRegistry.addMultiple({
+ isruleset: function (n) {
+ return isa(n, DetachedRuleset);
+ },
+ iscolor: function (n) {
+ return isa(n, Color);
+ },
+ isnumber: function (n) {
+ return isa(n, Dimension);
+ },
+ isstring: function (n) {
+ return isa(n, Quoted);
+ },
+ iskeyword: function (n) {
+ return isa(n, Keyword);
+ },
+ isurl: function (n) {
+ return isa(n, URL);
+ },
+ ispixel: function (n) {
+ return isunit(n, 'px');
+ },
+ ispercentage: function (n) {
+ return isunit(n, '%');
+ },
+ isem: function (n) {
+ return isunit(n, 'em');
+ },
+ isunit: isunit,
+ unit: function (val, unit) {
+ if (!(val instanceof Dimension)) {
+ throw { type: "Argument",
+ message: "the first argument to unit must be a number" +
+ (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
+ }
+ if (unit) {
+ if (unit instanceof Keyword) {
+ unit = unit.value;
+ } else {
+ unit = unit.toCSS();
+ }
+ } else {
+ unit = "";
+ }
+ return new Dimension(val.value, unit);
+ },
+ "get-unit": function (n) {
+ return new Anonymous(n.unit);
+ },
+ extract: function(values, index) {
+ index = index.value - 1; // (1-based index)
+
+ return getItemsFromNode(values)[index];
+ },
+ length: function(values) {
+ return new Dimension(getItemsFromNode(values).length);
+ }
+});
--- /dev/null
+var contexts = require("./contexts"),
+ Parser = require('./parser/parser');
+
+module.exports = function(environment) {
+
+ // FileInfo = {
+ // 'relativeUrls' - option - whether to adjust URL's to be relative
+ // 'filename' - full resolved filename of current file
+ // 'rootpath' - path to append to normal URLs for this node
+ // 'currentDirectory' - path to the current file, absolute
+ // 'rootFilename' - filename of the base file
+ // 'entryPath' - absolute path to the entry file
+ // 'reference' - whether the file should not be output and only output parts that are referenced
+
+ var ImportManager = function(context, rootFileInfo) {
+ this.rootFilename = rootFileInfo.filename;
+ this.paths = context.paths || []; // Search paths, when importing
+ this.contents = {}; // map - filename to contents of all the files
+ this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore
+ this.mime = context.mime;
+ this.error = null;
+ this.context = context;
+ // Deprecated? Unused outside of here, could be useful.
+ this.queue = []; // Files which haven't been imported yet
+ this.files = {}; // Holds the imported parse trees.
+ };
+ /**
+ * Add an import to be imported
+ * @param path - the raw path
+ * @param tryAppendLessExtension - whether to try appending the less extension (if the path has no extension)
+ * @param currentFileInfo - the current file info (used for instance to work out relative paths)
+ * @param importOptions - import options
+ * @param callback - callback for when it is imported
+ */
+ ImportManager.prototype.push = function (path, tryAppendLessExtension, currentFileInfo, importOptions, callback) {
+ var importManager = this;
+ this.queue.push(path);
+
+ var fileParsedFunc = function (e, root, fullPath) {
+ importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
+
+ var importedEqualsRoot = fullPath === importManager.rootFilename;
+ if (importOptions.optional && e) {
+ callback(null, {rules:[]}, false, null);
+ }
+ else {
+ importManager.files[fullPath] = root;
+ if (e && !importManager.error) { importManager.error = e; }
+ callback(e, root, importedEqualsRoot, fullPath);
+ }
+ };
+
+ var newFileInfo = {
+ relativeUrls: this.context.relativeUrls,
+ entryPath: currentFileInfo.entryPath,
+ rootpath: currentFileInfo.rootpath,
+ rootFilename: currentFileInfo.rootFilename
+ };
+
+ var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment);
+
+ if (!fileManager) {
+ fileParsedFunc({ message: "Could not find a file-manager for " + path });
+ return;
+ }
+
+ if (tryAppendLessExtension) {
+ path = fileManager.tryAppendLessExtension(path);
+ }
+
+ var loadFileCallback = function(loadedFile) {
+ var resolvedFilename = loadedFile.filename,
+ contents = loadedFile.contents.replace(/^\uFEFF/, '');
+
+ // Pass on an updated rootpath if path of imported file is relative and file
+ // is in a (sub|sup) directory
+ //
+ // Examples:
+ // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
+ // then rootpath should become 'less/module/nav/'
+ // - If path of imported file is '../mixins.less' and rootpath is 'less/',
+ // then rootpath should become 'less/../'
+ newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
+ if (newFileInfo.relativeUrls) {
+ newFileInfo.rootpath = fileManager.join(
+ (importManager.context.rootpath || ""),
+ fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
+
+ if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
+ newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
+ }
+ }
+ newFileInfo.filename = resolvedFilename;
+
+ var newEnv = new contexts.Parse(importManager.context);
+
+ newEnv.processImports = false;
+ importManager.contents[resolvedFilename] = contents;
+
+ if (currentFileInfo.reference || importOptions.reference) {
+ newFileInfo.reference = true;
+ }
+
+ if (importOptions.inline) {
+ fileParsedFunc(null, contents, resolvedFilename);
+ } else {
+ new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) {
+ fileParsedFunc(e, root, resolvedFilename);
+ });
+ }
+ };
+
+ var promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment,
+ function(err, loadedFile) {
+ if (err) {
+ fileParsedFunc(err);
+ } else {
+ loadFileCallback(loadedFile);
+ }
+ });
+ if (promise) {
+ promise.then(loadFileCallback, fileParsedFunc);
+ }
+ };
+ return ImportManager;
+};
--- /dev/null
+module.exports = function(environment, fileManagers) {
+ var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
+
+ var less = {
+ version: [2, 4, 0],
+ data: require('./data'),
+ tree: require('./tree'),
+ Environment: (Environment = require("./environment/environment")),
+ AbstractFileManager: require("./environment/abstract-file-manager"),
+ environment: (environment = new Environment(environment, fileManagers)),
+ visitors: require('./visitors'),
+ Parser: require('./parser/parser'),
+ functions: require('./functions')(environment),
+ contexts: require("./contexts"),
+ SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)),
+ SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput, environment)),
+ ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)),
+ ImportManager: (ImportManager = require('./import-manager')(environment)),
+ render: require("./render")(environment, ParseTree, ImportManager),
+ parse: require("./parse")(environment, ParseTree, ImportManager),
+ LessError: require('./less-error'),
+ transformTree: require('./transform-tree'),
+ utils: require('./utils'),
+ PluginManager: require('./plugin-manager'),
+ logger: require('./logger')
+ };
+
+ return less;
+};
--- /dev/null
+var utils = require("./utils");
+
+var LessError = module.exports = function LessError(e, importManager, currentFilename) {
+
+ Error.call(this);
+
+ var filename = e.filename || currentFilename;
+
+ if (importManager && filename) {
+ var input = importManager.contents[filename],
+ loc = utils.getLocation(e.index, input),
+ line = loc.line,
+ col = loc.column,
+ callLine = e.call && utils.getLocation(e.call, input).line,
+ lines = input.split('\n');
+
+ this.type = e.type || 'Syntax';
+ this.filename = filename;
+ this.index = e.index;
+ this.line = typeof line === 'number' ? line + 1 : null;
+ this.callLine = callLine + 1;
+ this.callExtract = lines[callLine];
+ this.column = col;
+ this.extract = [
+ lines[line - 1],
+ lines[line],
+ lines[line + 1]
+ ];
+ }
+ this.message = e.message;
+ this.stack = e.stack;
+};
+
+if (typeof Object.create === 'undefined') {
+ var F = function () {};
+ F.prototype = Error.prototype;
+ LessError.prototype = new F();
+} else {
+ LessError.prototype = Object.create(Error.prototype);
+}
+
+LessError.prototype.constructor = LessError;
--- /dev/null
+module.exports = {
+ error: function(msg) {
+ this._fireEvent("error", msg);
+ },
+ warn: function(msg) {
+ this._fireEvent("warn", msg);
+ },
+ info: function(msg) {
+ this._fireEvent("info", msg);
+ },
+ debug: function(msg) {
+ this._fireEvent("debug", msg);
+ },
+ addListener: function(listener) {
+ this._listeners.push(listener);
+ },
+ removeListener: function(listener) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ if (this._listeners[i] === listener) {
+ this._listeners.splice(i, 1);
+ return;
+ }
+ }
+ },
+ _fireEvent: function(type, msg) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ var logFunction = this._listeners[i][type];
+ if (logFunction) {
+ logFunction(msg);
+ }
+ }
+ },
+ _listeners: []
+};
--- /dev/null
+var LessError = require('./less-error'),
+ transformTree = require("./transform-tree"),
+ logger = require("./logger");
+
+module.exports = function(SourceMapBuilder) {
+ var ParseTree = function(root, imports) {
+ this.root = root;
+ this.imports = imports;
+ };
+
+ ParseTree.prototype.toCSS = function(options) {
+ var evaldRoot, result = {}, sourceMapBuilder;
+ try {
+ evaldRoot = transformTree(this.root, options);
+ } catch (e) {
+ throw new LessError(e, this.imports);
+ }
+
+ try {
+ var compress = Boolean(options.compress);
+ if (compress) {
+ logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
+ }
+
+ var toCSSOptions = {
+ compress: compress,
+ dumpLineNumbers: options.dumpLineNumbers,
+ strictUnits: Boolean(options.strictUnits),
+ numPrecision: 8};
+
+ if (options.sourceMap) {
+ sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
+ result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
+ } else {
+ result.css = evaldRoot.toCSS(toCSSOptions);
+ }
+ } catch (e) {
+ throw new LessError(e, this.imports);
+ }
+
+ if (options.pluginManager) {
+ var postProcessors = options.pluginManager.getPostProcessors();
+ for (var i = 0; i < postProcessors.length; i++) {
+ result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports });
+ }
+ }
+ if (options.sourceMap) {
+ result.map = sourceMapBuilder.getExternalSourceMap();
+ }
+
+ result.imports = [];
+ for (var file in this.imports.files) {
+ if (this.imports.files.hasOwnProperty(file) && file !== this.imports.rootFilename) {
+ result.imports.push(file);
+ }
+ }
+ return result;
+ };
+ return ParseTree;
+};
--- /dev/null
+var PromiseConstructor,
+ contexts = require("./contexts"),
+ Parser = require('./parser/parser'),
+ PluginManager = require('./plugin-manager');
+
+module.exports = function(environment, ParseTree, ImportManager) {
+ var parse = function (input, options, callback) {
+ options = options || {};
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ if (!callback) {
+ if (!PromiseConstructor) {
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
+ }
+ var self = this;
+ return new PromiseConstructor(function (resolve, reject) {
+ parse.call(self, input, options, function(err, output) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(output);
+ }
+ });
+ });
+ } else {
+ var context,
+ rootFileInfo,
+ pluginManager = new PluginManager(this);
+
+ pluginManager.addPlugins(options.plugins);
+ options.pluginManager = pluginManager;
+
+ context = new contexts.Parse(options);
+
+ if (options.rootFileInfo) {
+ rootFileInfo = options.rootFileInfo;
+ } else {
+ var filename = options.filename || "input";
+ var entryPath = filename.replace(/[^\/\\]*$/, "");
+ rootFileInfo = {
+ filename: filename,
+ relativeUrls: context.relativeUrls,
+ rootpath: context.rootpath || "",
+ currentDirectory: entryPath,
+ entryPath: entryPath,
+ rootFilename: filename
+ };
+ // add in a missing trailing slash
+ if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== "/") {
+ rootFileInfo.rootpath += "/";
+ }
+ }
+
+ var imports = new ImportManager(context, rootFileInfo);
+
+ new Parser(context, imports, rootFileInfo)
+ .parse(input, function (e, root) {
+ if (e) { return callback(e); }
+ callback(null, root, imports, options);
+ }, options);
+ }
+ };
+ return parse;
+};
--- /dev/null
+// Split the input into chunks.
+module.exports = function (input, fail) {
+ var len = input.length, level = 0, parenLevel = 0,
+ lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
+ chunks = [], emitFrom = 0,
+ chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
+
+ function emitChunk(force) {
+ var len = chunkerCurrentIndex - emitFrom;
+ if (((len < 512) && !force) || !len) {
+ return;
+ }
+ chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1));
+ emitFrom = chunkerCurrentIndex + 1;
+ }
+
+ for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
+ cc = input.charCodeAt(chunkerCurrentIndex);
+ if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
+ // a-z or whitespace
+ continue;
+ }
+
+ switch (cc) {
+ case 40: // (
+ parenLevel++;
+ lastOpeningParen = chunkerCurrentIndex;
+ continue;
+ case 41: // )
+ if (--parenLevel < 0) {
+ return fail("missing opening `(`", chunkerCurrentIndex);
+ }
+ continue;
+ case 59: // ;
+ if (!parenLevel) { emitChunk(); }
+ continue;
+ case 123: // {
+ level++;
+ lastOpening = chunkerCurrentIndex;
+ continue;
+ case 125: // }
+ if (--level < 0) {
+ return fail("missing opening `{`", chunkerCurrentIndex);
+ }
+ if (!level && !parenLevel) { emitChunk(); }
+ continue;
+ case 92: // \
+ if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; }
+ return fail("unescaped `\\`", chunkerCurrentIndex);
+ case 34:
+ case 39:
+ case 96: // ", ' and `
+ matched = 0;
+ currentChunkStartIndex = chunkerCurrentIndex;
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
+ if (cc2 > 96) { continue; }
+ if (cc2 == cc) { matched = 1; break; }
+ if (cc2 == 92) { // \
+ if (chunkerCurrentIndex == len - 1) {
+ return fail("unescaped `\\`", chunkerCurrentIndex);
+ }
+ chunkerCurrentIndex++;
+ }
+ }
+ if (matched) { continue; }
+ return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
+ case 47: // /, check for comment
+ if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; }
+ cc2 = input.charCodeAt(chunkerCurrentIndex + 1);
+ if (cc2 == 47) {
+ // //, find lnfeed
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
+ if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
+ }
+ } else if (cc2 == 42) {
+ // /*, find */
+ lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex;
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) {
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
+ if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; }
+ if (cc2 != 42) { continue; }
+ if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; }
+ }
+ if (chunkerCurrentIndex == len - 1) {
+ return fail("missing closing `*/`", currentChunkStartIndex);
+ }
+ chunkerCurrentIndex++;
+ }
+ continue;
+ case 42: // *, check for unmatched */
+ if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) {
+ return fail("unmatched `/*`", chunkerCurrentIndex);
+ }
+ continue;
+ }
+ }
+
+ if (level !== 0) {
+ if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
+ return fail("missing closing `}` or `*/`", lastOpening);
+ } else {
+ return fail("missing closing `}`", lastOpening);
+ }
+ } else if (parenLevel !== 0) {
+ return fail("missing closing `)`", lastOpeningParen);
+ }
+
+ emitChunk(true);
+ return chunks;
+};
--- /dev/null
+var chunker = require('./chunker');
+
+module.exports = function() {
+ var input, // LeSS input string
+ j, // current chunk
+ saveStack = [], // holds state for backtracking
+ furthest, // furthest index the parser has gone to
+ furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause
+ chunks, // chunkified input
+ current, // current chunk
+ currentPos, // index of current chunk, in `input`
+ parserInput = {};
+
+ parserInput.save = function() {
+ currentPos = parserInput.i;
+ saveStack.push( { current: current, i: parserInput.i, j: j });
+ };
+ parserInput.restore = function(possibleErrorMessage) {
+
+ if (parserInput.i > furthest || (parserInput.i === furthest && possibleErrorMessage && !furthestPossibleErrorMessage)) {
+ furthest = parserInput.i;
+ furthestPossibleErrorMessage = possibleErrorMessage;
+ }
+ var state = saveStack.pop();
+ current = state.current;
+ currentPos = parserInput.i = state.i;
+ j = state.j;
+ };
+ parserInput.forget = function() {
+ saveStack.pop();
+ };
+ function sync() {
+ if (parserInput.i > currentPos) {
+ current = current.slice(parserInput.i - currentPos);
+ currentPos = parserInput.i;
+ }
+ }
+ parserInput.isWhitespace = function (offset) {
+ var pos = parserInput.i + (offset || 0),
+ code = input.charCodeAt(pos);
+ return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF);
+ };
+ //
+ // Parse from a token, regexp or string, and move forward if match
+ //
+ parserInput.$ = function(tok) {
+ var tokType = typeof tok,
+ match, length;
+
+ // Either match a single character in the input,
+ // or match a regexp in the current chunk (`current`).
+ //
+ if (tokType === "string") {
+ if (input.charAt(parserInput.i) !== tok) {
+ return null;
+ }
+ skipWhitespace(1);
+ return tok;
+ }
+
+ // regexp
+ sync();
+ if (! (match = tok.exec(current))) {
+ return null;
+ }
+
+ length = match[0].length;
+
+ // The match is confirmed, add the match length to `i`,
+ // and consume any extra white-space characters (' ' || '\n')
+ // which come after that. The reason for this is that LeSS's
+ // grammar is mostly white-space insensitive.
+ //
+ skipWhitespace(length);
+
+ if (typeof match === 'string') {
+ return match;
+ } else {
+ return match.length === 1 ? match[0] : match;
+ }
+ };
+
+ // Specialization of $(tok)
+ parserInput.$re = function(tok) {
+ if (parserInput.i > currentPos) {
+ current = current.slice(parserInput.i - currentPos);
+ currentPos = parserInput.i;
+ }
+ var m = tok.exec(current);
+ if (!m) {
+ return null;
+ }
+
+ skipWhitespace(m[0].length);
+ if (typeof m === "string") {
+ return m;
+ }
+
+ return m.length === 1 ? m[0] : m;
+ };
+
+ // Specialization of $(tok)
+ parserInput.$char = function(tok) {
+ if (input.charAt(parserInput.i) !== tok) {
+ return null;
+ }
+ skipWhitespace(1);
+ return tok;
+ };
+
+ var CHARCODE_SPACE = 32,
+ CHARCODE_TAB = 9,
+ CHARCODE_LF = 10,
+ CHARCODE_CR = 13,
+ CHARCODE_PLUS = 43,
+ CHARCODE_COMMA = 44,
+ CHARCODE_FORWARD_SLASH = 47,
+ CHARCODE_9 = 57;
+
+ parserInput.autoCommentAbsorb = true;
+ parserInput.commentStore = [];
+ parserInput.finished = false;
+
+ var skipWhitespace = function(length) {
+ var oldi = parserInput.i, oldj = j,
+ curr = parserInput.i - currentPos,
+ endIndex = parserInput.i + current.length - curr,
+ mem = (parserInput.i += length),
+ inp = input,
+ c, nextChar, comment;
+
+ for (; parserInput.i < endIndex; parserInput.i++) {
+ c = inp.charCodeAt(parserInput.i);
+
+ if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
+ nextChar = inp.charAt(parserInput.i + 1);
+ if (nextChar === '/') {
+ comment = {index: parserInput.i, isLineComment: true};
+ var nextNewLine = inp.indexOf("\n", parserInput.i + 1);
+ if (nextNewLine < 0) {
+ nextNewLine = endIndex;
+ }
+ parserInput.i = nextNewLine;
+ comment.text = inp.substr(comment.i, parserInput.i - comment.i);
+ parserInput.commentStore.push(comment);
+ continue;
+ } else if (nextChar === '*') {
+ var haystack = inp.substr(parserInput.i);
+ var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//);
+ if (comment_search_result) {
+ comment = {
+ index: parserInput.i,
+ text: comment_search_result[0],
+ isLineComment: false
+ };
+ parserInput.i += comment.text.length - 1;
+ parserInput.commentStore.push(comment);
+ continue;
+ }
+ }
+ break;
+ }
+
+ if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
+ break;
+ }
+ }
+
+ current = current.slice(length + parserInput.i - mem + curr);
+ currentPos = parserInput.i;
+
+ if (!current.length) {
+ if (j < chunks.length - 1)
+ {
+ current = chunks[++j];
+ skipWhitespace(0); // skip space at the beginning of a chunk
+ return true; // things changed
+ }
+ parserInput.finished = true;
+ }
+
+ return oldi !== parserInput.i || oldj !== j;
+ };
+
+ // Same as $(), but don't change the state of the parser,
+ // just return the match.
+ parserInput.peek = function(tok) {
+ if (typeof tok === 'string') {
+ return input.charAt(parserInput.i) === tok;
+ } else {
+ return tok.test(current);
+ }
+ };
+
+ // Specialization of peek()
+ // TODO remove or change some currentChar calls to peekChar
+ parserInput.peekChar = function(tok) {
+ return input.charAt(parserInput.i) === tok;
+ };
+
+ parserInput.currentChar = function() {
+ return input.charAt(parserInput.i);
+ };
+
+ parserInput.getInput = function() {
+ return input;
+ };
+
+ parserInput.peekNotNumeric = function() {
+ var c = input.charCodeAt(parserInput.i);
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
+ return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
+ };
+
+ parserInput.start = function(str, chunkInput, failFunction) {
+ input = str;
+ parserInput.i = j = currentPos = furthest = 0;
+
+ // chunking apparantly makes things quicker (but my tests indicate
+ // it might actually make things slower in node at least)
+ // and it is a non-perfect parse - it can't recognise
+ // unquoted urls, meaning it can't distinguish comments
+ // meaning comments with quotes or {}() in them get 'counted'
+ // and then lead to parse errors.
+ // In addition if the chunking chunks in the wrong place we might
+ // not be able to parse a parser statement in one go
+ // this is officially deprecated but can be switched on via an option
+ // in the case it causes too much performance issues.
+ if (chunkInput) {
+ chunks = chunker(str, failFunction);
+ } else {
+ chunks = [str];
+ }
+
+ current = chunks[0];
+
+ skipWhitespace(0);
+ };
+
+ parserInput.end = function() {
+ var message,
+ isFinished = parserInput.i >= input.length;
+
+ if (parserInput.i < furthest) {
+ message = furthestPossibleErrorMessage;
+ parserInput.i = furthest;
+ }
+ return {
+ isFinished: isFinished,
+ furthest: parserInput.i,
+ furthestPossibleErrorMessage: message,
+ furthestReachedEnd: parserInput.i >= input.length - 1,
+ furthestChar: input[parserInput.i]
+ };
+ };
+
+ return parserInput;
+};
--- /dev/null
+var LessError = require('../less-error'),
+ tree = require("../tree"),
+ visitors = require("../visitors"),
+ getParserInput = require("./parser-input"),
+ utils = require("../utils");
+
+//
+// less.js - parser
+//
+// A relatively straight-forward predictive parser.
+// There is no tokenization/lexing stage, the input is parsed
+// in one sweep.
+//
+// To make the parser fast enough to run in the browser, several
+// optimization had to be made:
+//
+// - Matching and slicing on a huge input is often cause of slowdowns.
+// The solution is to chunkify the input into smaller strings.
+// The chunks are stored in the `chunks` var,
+// `j` holds the current chunk index, and `currentPos` holds
+// the index of the current chunk in relation to `input`.
+// This gives us an almost 4x speed-up.
+//
+// - In many cases, we don't need to match individual tokens;
+// for example, if a value doesn't hold any variables, operations
+// or dynamic references, the parser can effectively 'skip' it,
+// treating it as a literal.
+// An example would be '1px solid #000' - which evaluates to itself,
+// we don't need to know what the individual components are.
+// The drawback, of course is that you don't get the benefits of
+// syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
+// and a smaller speed-up in the code-gen.
+//
+//
+// Token matching is done with the `$` function, which either takes
+// a terminal string or regexp, or a non-terminal function to call.
+// It also takes care of moving all the indices forwards.
+//
+//
+var Parser = function Parser(context, imports, fileInfo) {
+ var parsers,
+ parserInput = getParserInput();
+
+ function expect(arg, msg, index) {
+ // some older browsers return typeof 'function' for RegExp
+ var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg);
+ if (result) {
+ return result;
+ }
+ error(msg || (typeof arg === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'"
+ : "unexpected token"));
+ }
+
+ // Specialization of expect()
+ function expectChar(arg, msg) {
+ if (parserInput.$char(arg)) {
+ return arg;
+ }
+ error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'");
+ }
+
+ function error(msg, type) {
+ throw new LessError(
+ {
+ index: parserInput.i,
+ filename: fileInfo.filename,
+ type: type || 'Syntax',
+ message: msg
+ },
+ imports
+ );
+ }
+
+ function getDebugInfo(index) {
+ var filename = fileInfo.filename;
+
+ return {
+ lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1,
+ fileName: filename
+ };
+ }
+
+ //
+ // The Parser
+ //
+ return {
+
+ //
+ // Parse an input string into an abstract syntax tree,
+ // @param str A string containing 'less' markup
+ // @param callback call `callback` when done.
+ // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
+ //
+ parse: function (str, callback, additionalData) {
+ var root, error = null, globalVars, modifyVars, ignored, preText = "";
+
+ globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : '';
+ modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : '';
+
+ if (context.pluginManager) {
+ var preProcessors = context.pluginManager.getPreProcessors();
+ for (var i = 0; i < preProcessors.length; i++) {
+ str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo });
+ }
+ }
+
+ if (globalVars || (additionalData && additionalData.banner)) {
+ preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
+ ignored = imports.contentsIgnoredChars;
+ ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0;
+ ignored[fileInfo.filename] += preText.length;
+ }
+
+ str = str.replace(/\r\n?/g, '\n');
+ // Remove potential UTF Byte Order Mark
+ str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
+ imports.contents[fileInfo.filename] = str;
+
+ // Start with the primary rule.
+ // The whole syntax tree is held under a Ruleset node,
+ // with the `root` property set to true, so no `{}` are
+ // output. The callback is called when the input is parsed.
+ try {
+ parserInput.start(str, context.chunkInput, function fail(msg, index) {
+ throw LessError({
+ index: index,
+ type: 'Parse',
+ message: msg,
+ filename: fileInfo.filename
+ }, imports);
+ });
+
+ root = new(tree.Ruleset)(null, this.parsers.primary());
+ root.root = true;
+ root.firstRoot = true;
+ } catch (e) {
+ return callback(new LessError(e, imports, fileInfo.filename));
+ }
+
+ // If `i` is smaller than the `input.length - 1`,
+ // it means the parser wasn't able to parse the whole
+ // string, so we've got a parsing error.
+ //
+ // We try to extract a \n delimited string,
+ // showing the line where the parse error occurred.
+ // We split it up into two parts (the part which parsed,
+ // and the part which didn't), so we can color them differently.
+ var endInfo = parserInput.end();
+ if (!endInfo.isFinished) {
+
+ var message = endInfo.furthestPossibleErrorMessage;
+
+ if (!message) {
+ message = "Unrecognised input";
+ if (endInfo.furthestChar === '}') {
+ message += ". Possibly missing opening '{'";
+ } else if (endInfo.furthestChar === ')') {
+ message += ". Possibly missing opening '('";
+ } else if (endInfo.furthestReachedEnd) {
+ message += ". Possibly missing something";
+ }
+ }
+
+ error = new LessError({
+ type: "Parse",
+ message: message,
+ index: endInfo.furthest,
+ filename: fileInfo.filename
+ }, imports);
+ }
+
+ var finish = function (e) {
+ e = error || e || imports.error;
+
+ if (e) {
+ if (!(e instanceof LessError)) {
+ e = new LessError(e, imports, fileInfo.filename);
+ }
+
+ return callback(e);
+ }
+ else {
+ return callback(null, root);
+ }
+ };
+
+ if (context.processImports !== false) {
+ new visitors.ImportVisitor(imports, finish)
+ .run(root);
+ } else {
+ return finish();
+ }
+ },
+
+ //
+ // Here in, the parsing rules/functions
+ //
+ // The basic structure of the syntax tree generated is as follows:
+ //
+ // Ruleset -> Rule -> Value -> Expression -> Entity
+ //
+ // Here's some Less code:
+ //
+ // .class {
+ // color: #fff;
+ // border: 1px solid #000;
+ // width: @w + 4px;
+ // > .child {...}
+ // }
+ //
+ // And here's what the parse tree might look like:
+ //
+ // Ruleset (Selector '.class', [
+ // Rule ("color", Value ([Expression [Color #fff]]))
+ // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
+ // Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
+ // Ruleset (Selector [Element '>', '.child'], [...])
+ // ])
+ //
+ // In general, most rules will try to parse a token with the `$()` function, and if the return
+ // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
+ // first, before parsing, that's when we use `peek()`.
+ //
+ parsers: parsers = {
+ //
+ // The `primary` rule is the *entry* and *exit* point of the parser.
+ // The rules here can appear at any level of the parse tree.
+ //
+ // The recursive nature of the grammar is an interplay between the `block`
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
+ // as represented by this simplified grammar:
+ //
+ // primary → (ruleset | rule)+
+ // ruleset → selector+ block
+ // block → '{' primary '}'
+ //
+ // Only at one point is the primary rule not called from the
+ // block rule: at the root level.
+ //
+ primary: function () {
+ var mixin = this.mixin, root = [], node;
+
+ while (true)
+ {
+ while (true) {
+ node = this.comment();
+ if (!node) { break; }
+ root.push(node);
+ }
+ // always process comments before deciding if finished
+ if (parserInput.finished) {
+ break;
+ }
+ if (parserInput.peek('}')) {
+ break;
+ }
+
+ node = this.extendRule();
+ if (node) {
+ root = root.concat(node);
+ continue;
+ }
+
+ node = mixin.definition() || this.rule() || this.ruleset() ||
+ mixin.call() || this.rulesetCall() || this.directive();
+ if (node) {
+ root.push(node);
+ } else {
+ if (!(parserInput.$re(/^[\s\n]+/) || parserInput.$re(/^;+/))) {
+ break;
+ }
+ }
+ }
+
+ return root;
+ },
+
+ // comments are collected by the main parsing mechanism and then assigned to nodes
+ // where the current structure allows it
+ comment: function () {
+ if (parserInput.commentStore.length) {
+ var comment = parserInput.commentStore.shift();
+ return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo);
+ }
+ },
+
+ //
+ // Entities are tokens which can be found inside an Expression
+ //
+ entities: {
+ //
+ // A string, which supports escaping " and '
+ //
+ // "milky way" 'he\'s the one!'
+ //
+ quoted: function () {
+ var str, index = parserInput.i;
+
+ str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/);
+ if (str) {
+ return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, fileInfo);
+ }
+ },
+
+ //
+ // A catch-all word, such as:
+ //
+ // black border-collapse
+ //
+ keyword: function () {
+ var k = parserInput.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);
+ if (k) {
+ return tree.Color.fromKeyword(k) || new(tree.Keyword)(k);
+ }
+ },
+
+ //
+ // A function call
+ //
+ // rgb(255, 0, 255)
+ //
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
+ // deal with the details.
+ //
+ // The arguments are parsed with the `entities.arguments` parser.
+ //
+ call: function () {
+ var name, nameLC, args, alpha, index = parserInput.i;
+
+ if (parserInput.peek(/^url\(/i)) {
+ return;
+ }
+
+ parserInput.save();
+
+ name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/);
+ if (!name) { parserInput.forget(); return; }
+
+ name = name[1];
+ nameLC = name.toLowerCase();
+
+ if (nameLC === 'alpha') {
+ alpha = parsers.alpha();
+ if (alpha) {
+ return alpha;
+ }
+ }
+
+ args = this.arguments();
+
+ if (! parserInput.$char(')')) {
+ parserInput.restore("Could not parse call arguments or missing ')'");
+ return;
+ }
+
+ parserInput.forget();
+ return new(tree.Call)(name, args, index, fileInfo);
+ },
+ arguments: function () {
+ var args = [], arg;
+
+ while (true) {
+ arg = this.assignment() || parsers.expression();
+ if (!arg) {
+ break;
+ }
+ args.push(arg);
+ if (! parserInput.$char(',')) {
+ break;
+ }
+ }
+ return args;
+ },
+ literal: function () {
+ return this.dimension() ||
+ this.color() ||
+ this.quoted() ||
+ this.unicodeDescriptor();
+ },
+
+ // Assignments are argument entities for calls.
+ // They are present in ie filter properties as shown below.
+ //
+ // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
+ //
+
+ assignment: function () {
+ var key, value;
+ key = parserInput.$re(/^\w+(?=\s?=)/i);
+ if (!key) {
+ return;
+ }
+ if (!parserInput.$char('=')) {
+ return;
+ }
+ value = parsers.entity();
+ if (value) {
+ return new(tree.Assignment)(key, value);
+ }
+ },
+
+ //
+ // Parse url() tokens
+ //
+ // We use a specific rule for urls, because they don't really behave like
+ // standard function calls. The difference is that the argument doesn't have
+ // to be enclosed within a string, so it can't be parsed as an Expression.
+ //
+ url: function () {
+ var value, index = parserInput.i;
+
+ parserInput.autoCommentAbsorb = false;
+
+ if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) {
+ parserInput.autoCommentAbsorb = true;
+ return;
+ }
+
+ value = this.quoted() || this.variable() ||
+ parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
+
+ parserInput.autoCommentAbsorb = true;
+
+ expectChar(')');
+
+ return new(tree.URL)((value.value != null || value instanceof tree.Variable) ?
+ value : new(tree.Anonymous)(value), index, fileInfo);
+ },
+
+ //
+ // A Variable entity, such as `@fink`, in
+ //
+ // width: @fink + 2px
+ //
+ // We use a different parser for variable definitions,
+ // see `parsers.variable`.
+ //
+ variable: function () {
+ var name, index = parserInput.i;
+
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
+ return new(tree.Variable)(name, index, fileInfo);
+ }
+ },
+
+ // A variable entity useing the protective {} e.g. @{var}
+ variableCurly: function () {
+ var curly, index = parserInput.i;
+
+ if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
+ return new(tree.Variable)("@" + curly[1], index, fileInfo);
+ }
+ },
+
+ //
+ // A Hexadecimal color
+ //
+ // #4F3C2F
+ //
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
+ //
+ color: function () {
+ var rgb;
+
+ if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
+ // strip colons, brackets, whitespaces and other characters that should not
+ // definitely be part of color string
+ var colorCandidateString = rgb.input.match(/^#([\w]+).*/);
+ colorCandidateString = colorCandidateString[1];
+ if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
+ error("Invalid HEX color code");
+ }
+ return new(tree.Color)(rgb[1]);
+ }
+ },
+
+ //
+ // A Dimension, that is, a number and a unit
+ //
+ // 0.5em 95%
+ //
+ dimension: function () {
+ if (parserInput.peekNotNumeric()) {
+ return;
+ }
+
+ var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i);
+ if (value) {
+ return new(tree.Dimension)(value[1], value[2]);
+ }
+ },
+
+ //
+ // A unicode descriptor, as is used in unicode-range
+ //
+ // U+0?? or U+00A1-00A9
+ //
+ unicodeDescriptor: function () {
+ var ud;
+
+ ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
+ if (ud) {
+ return new(tree.UnicodeDescriptor)(ud[0]);
+ }
+ },
+
+ //
+ // JavaScript code to be evaluated
+ //
+ // `window.location.href`
+ //
+ javascript: function () {
+ var js, index = parserInput.i;
+
+ js = parserInput.$re(/^(~)?`([^`]*)`/);
+ if (js) {
+ return new(tree.JavaScript)(js[2], Boolean(js[1]), index, fileInfo);
+ }
+ }
+ },
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink:
+ //
+ variable: function () {
+ var name;
+
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
+ },
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink();
+ //
+ rulesetCall: function () {
+ var name;
+
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
+ return new tree.RulesetCall(name[1]);
+ }
+ },
+
+ //
+ // extend syntax - used to extend selectors
+ //
+ extend: function(isRule) {
+ var elements, e, index = parserInput.i, option, extendList, extend;
+
+ if (!(isRule ? parserInput.$re(/^&:extend\(/) : parserInput.$re(/^:extend\(/))) { return; }
+
+ do {
+ option = null;
+ elements = null;
+ while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
+ e = this.element();
+ if (!e) {
+ break;
+ }
+ if (elements) {
+ elements.push(e);
+ } else {
+ elements = [ e ];
+ }
+ }
+
+ option = option && option[1];
+ if (!elements) {
+ error("Missing target selector for :extend().");
+ }
+ extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
+ if (extendList) {
+ extendList.push(extend);
+ } else {
+ extendList = [ extend ];
+ }
+ } while (parserInput.$char(","));
+
+ expect(/^\)/);
+
+ if (isRule) {
+ expect(/^;/);
+ }
+
+ return extendList;
+ },
+
+ //
+ // extendRule - used in a rule to extend all the parent selectors
+ //
+ extendRule: function() {
+ return this.extend(true);
+ },
+
+ //
+ // Mixins
+ //
+ mixin: {
+ //
+ // A Mixin call, with an optional argument list
+ //
+ // #mixins > .square(#fff);
+ // .rounded(4px, black);
+ // .button;
+ //
+ // The `while` loop is there because mixins can be
+ // namespaced, but we only support the child and descendant
+ // selector for now.
+ //
+ call: function () {
+ var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex,
+ elements, elem, e, c, args;
+
+ if (s !== '.' && s !== '#') { return; }
+
+ parserInput.save(); // stop us absorbing part of an invalid selector
+
+ while (true) {
+ elemIndex = parserInput.i;
+ e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
+ if (!e) {
+ break;
+ }
+ elem = new(tree.Element)(c, e, elemIndex, fileInfo);
+ if (elements) {
+ elements.push(elem);
+ } else {
+ elements = [ elem ];
+ }
+ c = parserInput.$char('>');
+ }
+
+ if (elements) {
+ if (parserInput.$char('(')) {
+ args = this.args(true).args;
+ expectChar(')');
+ }
+
+ if (parsers.important()) {
+ important = true;
+ }
+
+ if (parsers.end()) {
+ parserInput.forget();
+ return new(tree.mixin.Call)(elements, args, index, fileInfo, important);
+ }
+ }
+
+ parserInput.restore();
+ },
+ args: function (isCall) {
+ var entities = parsers.entities,
+ returner = { args:null, variadic: false },
+ expressions = [], argsSemiColon = [], argsComma = [],
+ isSemiColonSeparated, expressionContainsNamed, name, nameLoop, value, arg;
+
+ parserInput.save();
+
+ while (true) {
+ if (isCall) {
+ arg = parsers.detachedRuleset() || parsers.expression();
+ } else {
+ parserInput.commentStore.length = 0;
+ if (parserInput.currentChar() === '.' && parserInput.$re(/^\.{3}/)) {
+ returner.variadic = true;
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
+ isSemiColonSeparated = true;
+ }
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
+ .push({ variadic: true });
+ break;
+ }
+ arg = entities.variable() || entities.literal() || entities.keyword();
+ }
+
+ if (!arg) {
+ break;
+ }
+
+ nameLoop = null;
+ if (arg.throwAwayComments) {
+ arg.throwAwayComments();
+ }
+ value = arg;
+ var val = null;
+
+ if (isCall) {
+ // Variable
+ if (arg.value && arg.value.length == 1) {
+ val = arg.value[0];
+ }
+ } else {
+ val = arg;
+ }
+
+ if (val && val instanceof tree.Variable) {
+ if (parserInput.$char(':')) {
+ if (expressions.length > 0) {
+ if (isSemiColonSeparated) {
+ error("Cannot mix ; and , as delimiter types");
+ }
+ expressionContainsNamed = true;
+ }
+
+ // we do not support setting a ruleset as a default variable - it doesn't make sense
+ // However if we do want to add it, there is nothing blocking it, just don't error
+ // and remove isCall dependency below
+ value = (isCall && parsers.detachedRuleset()) || parsers.expression();
+
+ if (!value) {
+ if (isCall) {
+ error("could not understand value for named argument");
+ } else {
+ parserInput.restore();
+ returner.args = [];
+ return returner;
+ }
+ }
+ nameLoop = (name = val.name);
+ } else if (!isCall && parserInput.$re(/^\.{3}/)) {
+ returner.variadic = true;
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
+ isSemiColonSeparated = true;
+ }
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
+ .push({ name: arg.name, variadic: true });
+ break;
+ } else if (!isCall) {
+ name = nameLoop = val.name;
+ value = null;
+ }
+ }
+
+ if (value) {
+ expressions.push(value);
+ }
+
+ argsComma.push({ name:nameLoop, value:value });
+
+ if (parserInput.$char(',')) {
+ continue;
+ }
+
+ if (parserInput.$char(';') || isSemiColonSeparated) {
+
+ if (expressionContainsNamed) {
+ error("Cannot mix ; and , as delimiter types");
+ }
+
+ isSemiColonSeparated = true;
+
+ if (expressions.length > 1) {
+ value = new(tree.Value)(expressions);
+ }
+ argsSemiColon.push({ name:name, value:value });
+
+ name = null;
+ expressions = [];
+ expressionContainsNamed = false;
+ }
+ }
+
+ parserInput.forget();
+ returner.args = isSemiColonSeparated ? argsSemiColon : argsComma;
+ return returner;
+ },
+ //
+ // A Mixin definition, with a list of parameters
+ //
+ // .rounded (@radius: 2px, @color) {
+ // ...
+ // }
+ //
+ // Until we have a finer grained state-machine, we have to
+ // do a look-ahead, to make sure we don't have a mixin call.
+ // See the `rule` function for more information.
+ //
+ // We start by matching `.rounded (`, and then proceed on to
+ // the argument list, which has optional default values.
+ // We store the parameters in `params`, with a `value` key,
+ // if there is a value, such as in the case of `@radius`.
+ //
+ // Once we've got our params list, and a closing `)`, we parse
+ // the `{...}` block.
+ //
+ definition: function () {
+ var name, params = [], match, ruleset, cond, variadic = false;
+ if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
+ parserInput.peek(/^[^{]*\}/)) {
+ return;
+ }
+
+ parserInput.save();
+
+ match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
+ if (match) {
+ name = match[1];
+
+ var argInfo = this.args(false);
+ params = argInfo.args;
+ variadic = argInfo.variadic;
+
+ // .mixincall("@{a}");
+ // looks a bit like a mixin definition..
+ // also
+ // .mixincall(@a: {rule: set;});
+ // so we have to be nice and restore
+ if (!parserInput.$char(')')) {
+ parserInput.restore("Missing closing ')'");
+ return;
+ }
+
+ parserInput.commentStore.length = 0;
+
+ if (parserInput.$re(/^when/)) { // Guard
+ cond = expect(parsers.conditions, 'expected condition');
+ }
+
+ ruleset = parsers.block();
+
+ if (ruleset) {
+ parserInput.forget();
+ return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
+ } else {
+ parserInput.restore();
+ }
+ } else {
+ parserInput.forget();
+ }
+ }
+ },
+
+ //
+ // Entities are the smallest recognized token,
+ // and can be found inside a rule's value.
+ //
+ entity: function () {
+ var entities = this.entities;
+
+ return this.comment() || entities.literal() || entities.variable() || entities.url() ||
+ entities.call() || entities.keyword() || entities.javascript();
+ },
+
+ //
+ // A Rule terminator. Note that we use `peek()` to check for '}',
+ // because the `block` rule will be expecting it, but we still need to make sure
+ // it's there, if ';' was ommitted.
+ //
+ end: function () {
+ return parserInput.$char(';') || parserInput.peek('}');
+ },
+
+ //
+ // IE's alpha function
+ //
+ // alpha(opacity=88)
+ //
+ alpha: function () {
+ var value;
+
+ if (! parserInput.$re(/^opacity=/i)) { return; }
+ value = parserInput.$re(/^\d+/);
+ if (!value) {
+ value = expect(this.entities.variable, "Could not parse alpha");
+ }
+ expectChar(')');
+ return new(tree.Alpha)(value);
+ },
+
+ //
+ // A Selector Element
+ //
+ // div
+ // + h1
+ // #socks
+ // input[type="text"]
+ //
+ // Elements are the building blocks for Selectors,
+ // they are made out of a `Combinator` (see combinator rule),
+ // and an element name, such as a tag a class, or `*`.
+ //
+ element: function () {
+ var e, c, v, index = parserInput.i;
+
+ c = this.combinator();
+
+ e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
+ parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
+ parserInput.$char('*') || parserInput.$char('&') || this.attribute() ||
+ parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
+ this.entities.variableCurly();
+
+ if (! e) {
+ parserInput.save();
+ if (parserInput.$char('(')) {
+ if ((v = this.selector()) && parserInput.$char(')')) {
+ e = new(tree.Paren)(v);
+ parserInput.forget();
+ } else {
+ parserInput.restore("Missing closing ')'");
+ }
+ } else {
+ parserInput.forget();
+ }
+ }
+
+ if (e) { return new(tree.Element)(c, e, index, fileInfo); }
+ },
+
+ //
+ // Combinators combine elements together, in a Selector.
+ //
+ // Because our parser isn't white-space sensitive, special care
+ // has to be taken, when parsing the descendant combinator, ` `,
+ // as it's an empty space. We have to check the previous character
+ // in the input, to see if it's a ` ` character. More info on how
+ // we deal with this in *combinator.js*.
+ //
+ combinator: function () {
+ var c = parserInput.currentChar();
+
+ if (c === '/') {
+ parserInput.save();
+ var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i);
+ if (slashedCombinator) {
+ parserInput.forget();
+ return new(tree.Combinator)(slashedCombinator);
+ }
+ parserInput.restore();
+ }
+
+ if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
+ parserInput.i++;
+ if (c === '^' && parserInput.currentChar() === '^') {
+ c = '^^';
+ parserInput.i++;
+ }
+ while (parserInput.isWhitespace()) { parserInput.i++; }
+ return new(tree.Combinator)(c);
+ } else if (parserInput.isWhitespace(-1)) {
+ return new(tree.Combinator)(" ");
+ } else {
+ return new(tree.Combinator)(null);
+ }
+ },
+ //
+ // A CSS selector (see selector below)
+ // with less extensions e.g. the ability to extend and guard
+ //
+ lessSelector: function () {
+ return this.selector(true);
+ },
+ //
+ // A CSS Selector
+ //
+ // .class > div + h1
+ // li a:hover
+ //
+ // Selectors are made out of one or more Elements, see above.
+ //
+ selector: function (isLess) {
+ var index = parserInput.i, elements, extendList, c, e, allExtends, when, condition;
+
+ while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$re(/^when/))) || (e = this.element())) {
+ if (when) {
+ condition = expect(this.conditions, 'expected condition');
+ } else if (condition) {
+ error("CSS guard can only be used at the end of selector");
+ } else if (extendList) {
+ if (allExtends) {
+ allExtends = allExtends.concat(extendList);
+ } else {
+ allExtends = extendList;
+ }
+ } else {
+ if (allExtends) { error("Extend can only be used at the end of selector"); }
+ c = parserInput.currentChar();
+ if (elements) {
+ elements.push(e);
+ } else {
+ elements = [ e ];
+ }
+ e = null;
+ }
+ if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
+ break;
+ }
+ }
+
+ if (elements) { return new(tree.Selector)(elements, allExtends, condition, index, fileInfo); }
+ if (allExtends) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
+ },
+ attribute: function () {
+ if (! parserInput.$char('[')) { return; }
+
+ var entities = this.entities,
+ key, val, op;
+
+ if (!(key = entities.variableCurly())) {
+ key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
+ }
+
+ op = parserInput.$re(/^[|~*$^]?=/);
+ if (op) {
+ val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly();
+ }
+
+ expectChar(']');
+
+ return new(tree.Attribute)(key, op, val);
+ },
+
+ //
+ // The `block` rule is used by `ruleset` and `mixin.definition`.
+ // It's a wrapper around the `primary` rule, with added `{}`.
+ //
+ block: function () {
+ var content;
+ if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) {
+ return content;
+ }
+ },
+
+ blockRuleset: function() {
+ var block = this.block();
+
+ if (block) {
+ block = new tree.Ruleset(null, block);
+ }
+ return block;
+ },
+
+ detachedRuleset: function() {
+ var blockRuleset = this.blockRuleset();
+ if (blockRuleset) {
+ return new tree.DetachedRuleset(blockRuleset);
+ }
+ },
+
+ //
+ // div, .class, body > p {...}
+ //
+ ruleset: function () {
+ var selectors, s, rules, debugInfo;
+
+ parserInput.save();
+
+ if (context.dumpLineNumbers) {
+ debugInfo = getDebugInfo(parserInput.i);
+ }
+
+ while (true) {
+ s = this.lessSelector();
+ if (!s) {
+ break;
+ }
+ if (selectors) {
+ selectors.push(s);
+ } else {
+ selectors = [ s ];
+ }
+ parserInput.commentStore.length = 0;
+ if (s.condition && selectors.length > 1) {
+ error("Guards are only currently allowed on a single selector.");
+ }
+ if (! parserInput.$char(',')) { break; }
+ if (s.condition) {
+ error("Guards are only currently allowed on a single selector.");
+ }
+ parserInput.commentStore.length = 0;
+ }
+
+ if (selectors && (rules = this.block())) {
+ parserInput.forget();
+ var ruleset = new(tree.Ruleset)(selectors, rules, context.strictImports);
+ if (context.dumpLineNumbers) {
+ ruleset.debugInfo = debugInfo;
+ }
+ return ruleset;
+ } else {
+ parserInput.restore();
+ }
+ },
+ rule: function (tryAnonymous) {
+ var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable;
+
+ if (c === '.' || c === '#' || c === '&') { return; }
+
+ parserInput.save();
+
+ name = this.variable() || this.ruleProperty();
+ if (name) {
+ isVariable = typeof name === "string";
+
+ if (isVariable) {
+ value = this.detachedRuleset();
+ }
+
+ parserInput.commentStore.length = 0;
+ if (!value) {
+ // a name returned by this.ruleProperty() is always an array of the form:
+ // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
+ // where each item is a tree.Keyword or tree.Variable
+ merge = !isVariable && name.pop().value;
+
+ // prefer to try to parse first if its a variable or we are compressing
+ // but always fallback on the other one
+ var tryValueFirst = !tryAnonymous && (context.compress || isVariable);
+
+ if (tryValueFirst) {
+ value = this.value();
+ }
+ if (!value) {
+ value = this.anonymousValue();
+ if (value) {
+ parserInput.forget();
+ // anonymous values absorb the end ';' which is reequired for them to work
+ return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo);
+ }
+ }
+ if (!tryValueFirst && !value) {
+ value = this.value();
+ }
+
+ important = this.important();
+ }
+
+ if (value && this.end()) {
+ parserInput.forget();
+ return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo);
+ } else {
+ parserInput.restore();
+ if (value && !tryAnonymous) {
+ return this.rule(true);
+ }
+ }
+ } else {
+ parserInput.forget();
+ }
+ },
+ anonymousValue: function () {
+ var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/);
+ if (match) {
+ return new(tree.Anonymous)(match[1]);
+ }
+ },
+
+ //
+ // An @import directive
+ //
+ // @import "lib";
+ //
+ // Depending on our environment, importing is done differently:
+ // In the browser, it's an XHR request, in Node, it would be a
+ // file-system operation. The function used for importing is
+ // stored in `import`, which we pass to the Import constructor.
+ //
+ "import": function () {
+ var path, features, index = parserInput.i;
+
+ var dir = parserInput.$re(/^@import?\s+/);
+
+ if (dir) {
+ var options = (dir ? this.importOptions() : null) || {};
+
+ if ((path = this.entities.quoted() || this.entities.url())) {
+ features = this.mediaFeatures();
+
+ if (!parserInput.$(';')) {
+ parserInput.i = index;
+ error("missing semi-colon or unrecognised media features on import");
+ }
+ features = features && new(tree.Value)(features);
+ return new(tree.Import)(path, features, options, index, fileInfo);
+ }
+ else
+ {
+ parserInput.i = index;
+ error("malformed import statement");
+ }
+ }
+ },
+
+ importOptions: function() {
+ var o, options = {}, optionName, value;
+
+ // list of options, surrounded by parens
+ if (! parserInput.$char('(')) { return null; }
+ do {
+ o = this.importOption();
+ if (o) {
+ optionName = o;
+ value = true;
+ switch(optionName) {
+ case "css":
+ optionName = "less";
+ value = false;
+ break;
+ case "once":
+ optionName = "multiple";
+ value = false;
+ break;
+ }
+ options[optionName] = value;
+ if (! parserInput.$char(',')) { break; }
+ }
+ } while (o);
+ expectChar(')');
+ return options;
+ },
+
+ importOption: function() {
+ var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
+ if (opt) {
+ return opt[1];
+ }
+ },
+
+ mediaFeature: function () {
+ var entities = this.entities, nodes = [], e, p;
+ parserInput.save();
+ do {
+ e = entities.keyword() || entities.variable();
+ if (e) {
+ nodes.push(e);
+ } else if (parserInput.$char('(')) {
+ p = this.property();
+ e = this.value();
+ if (parserInput.$char(')')) {
+ if (p && e) {
+ nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true)));
+ } else if (e) {
+ nodes.push(new(tree.Paren)(e));
+ } else {
+ parserInput.restore("badly formed media feature definition");
+ return null;
+ }
+ } else {
+ parserInput.restore("Missing closing ')'");
+ return null;
+ }
+ }
+ } while (e);
+
+ parserInput.forget();
+ if (nodes.length > 0) {
+ return new(tree.Expression)(nodes);
+ }
+ },
+
+ mediaFeatures: function () {
+ var entities = this.entities, features = [], e;
+ do {
+ e = this.mediaFeature();
+ if (e) {
+ features.push(e);
+ if (! parserInput.$char(',')) { break; }
+ } else {
+ e = entities.variable();
+ if (e) {
+ features.push(e);
+ if (! parserInput.$char(',')) { break; }
+ }
+ }
+ } while (e);
+
+ return features.length > 0 ? features : null;
+ },
+
+ media: function () {
+ var features, rules, media, debugInfo;
+
+ if (context.dumpLineNumbers) {
+ debugInfo = getDebugInfo(parserInput.i);
+ }
+
+ if (parserInput.$re(/^@media/)) {
+ features = this.mediaFeatures();
+
+ rules = this.block();
+ if (rules) {
+ media = new(tree.Media)(rules, features, parserInput.i, fileInfo);
+ if (context.dumpLineNumbers) {
+ media.debugInfo = debugInfo;
+ }
+ return media;
+ }
+ }
+ },
+
+ //
+ // A CSS Directive
+ //
+ // @charset "utf-8";
+ //
+ directive: function () {
+ var index = parserInput.i, name, value, rules, nonVendorSpecificName,
+ hasIdentifier, hasExpression, hasUnknown, hasBlock = true;
+
+ if (parserInput.currentChar() !== '@') { return; }
+
+ value = this['import']() || this.media();
+ if (value) {
+ return value;
+ }
+
+ parserInput.save();
+
+ name = parserInput.$re(/^@[a-z-]+/);
+
+ if (!name) { return; }
+
+ nonVendorSpecificName = name;
+ if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
+ nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
+ }
+
+ switch(nonVendorSpecificName) {
+ /*
+ case "@font-face":
+ case "@viewport":
+ case "@top-left":
+ case "@top-left-corner":
+ case "@top-center":
+ case "@top-right":
+ case "@top-right-corner":
+ case "@bottom-left":
+ case "@bottom-left-corner":
+ case "@bottom-center":
+ case "@bottom-right":
+ case "@bottom-right-corner":
+ case "@left-top":
+ case "@left-middle":
+ case "@left-bottom":
+ case "@right-top":
+ case "@right-middle":
+ case "@right-bottom":
+ hasBlock = true;
+ break;
+ */
+ case "@counter-style":
+ hasIdentifier = true;
+ hasBlock = true;
+ break;
+ case "@charset":
+ hasIdentifier = true;
+ hasBlock = false;
+ break;
+ case "@namespace":
+ hasExpression = true;
+ hasBlock = false;
+ break;
+ case "@keyframes":
+ hasIdentifier = true;
+ break;
+ case "@host":
+ case "@page":
+ case "@document":
+ case "@supports":
+ hasUnknown = true;
+ break;
+ }
+
+ parserInput.commentStore.length = 0;
+
+ if (hasIdentifier) {
+ value = this.entity();
+ if (!value) {
+ error("expected " + name + " identifier");
+ }
+ } else if (hasExpression) {
+ value = this.expression();
+ if (!value) {
+ error("expected " + name + " expression");
+ }
+ } else if (hasUnknown) {
+ value = (parserInput.$re(/^[^{;]+/) || '').trim();
+ if (value) {
+ value = new(tree.Anonymous)(value);
+ }
+ }
+
+ if (hasBlock) {
+ rules = this.blockRuleset();
+ }
+
+ if (rules || (!hasBlock && value && parserInput.$char(';'))) {
+ parserInput.forget();
+ return new(tree.Directive)(name, value, rules, index, fileInfo,
+ context.dumpLineNumbers ? getDebugInfo(index) : null);
+ }
+
+ parserInput.restore("directive options not recognised");
+ },
+
+ //
+ // A Value is a comma-delimited list of Expressions
+ //
+ // font-family: Baskerville, Georgia, serif;
+ //
+ // In a Rule, a Value represents everything after the `:`,
+ // and before the `;`.
+ //
+ value: function () {
+ var e, expressions = [];
+
+ do {
+ e = this.expression();
+ if (e) {
+ expressions.push(e);
+ if (! parserInput.$char(',')) { break; }
+ }
+ } while (e);
+
+ if (expressions.length > 0) {
+ return new(tree.Value)(expressions);
+ }
+ },
+ important: function () {
+ if (parserInput.currentChar() === '!') {
+ return parserInput.$re(/^! *important/);
+ }
+ },
+ sub: function () {
+ var a, e;
+
+ parserInput.save();
+ if (parserInput.$char('(')) {
+ a = this.addition();
+ if (a && parserInput.$char(')')) {
+ parserInput.forget();
+ e = new(tree.Expression)([a]);
+ e.parens = true;
+ return e;
+ }
+ parserInput.restore("Expected ')'");
+ return;
+ }
+ parserInput.restore();
+ },
+ multiplication: function () {
+ var m, a, op, operation, isSpaced;
+ m = this.operand();
+ if (m) {
+ isSpaced = parserInput.isWhitespace(-1);
+ while (true) {
+ if (parserInput.peek(/^\/[*\/]/)) {
+ break;
+ }
+
+ parserInput.save();
+
+ op = parserInput.$char('/') || parserInput.$char('*');
+
+ if (!op) { parserInput.forget(); break; }
+
+ a = this.operand();
+
+ if (!a) { parserInput.restore(); break; }
+ parserInput.forget();
+
+ m.parensInOp = true;
+ a.parensInOp = true;
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
+ isSpaced = parserInput.isWhitespace(-1);
+ }
+ return operation || m;
+ }
+ },
+ addition: function () {
+ var m, a, op, operation, isSpaced;
+ m = this.multiplication();
+ if (m) {
+ isSpaced = parserInput.isWhitespace(-1);
+ while (true) {
+ op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-')));
+ if (!op) {
+ break;
+ }
+ a = this.multiplication();
+ if (!a) {
+ break;
+ }
+
+ m.parensInOp = true;
+ a.parensInOp = true;
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
+ isSpaced = parserInput.isWhitespace(-1);
+ }
+ return operation || m;
+ }
+ },
+ conditions: function () {
+ var a, b, index = parserInput.i, condition;
+
+ a = this.condition();
+ if (a) {
+ while (true) {
+ if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) {
+ break;
+ }
+ b = this.condition();
+ if (!b) {
+ break;
+ }
+ condition = new(tree.Condition)('or', condition || a, b, index);
+ }
+ return condition || a;
+ }
+ },
+ condition: function () {
+ var entities = this.entities, index = parserInput.i, negate = false,
+ a, b, c, op;
+
+ if (parserInput.$re(/^not/)) { negate = true; }
+ expectChar('(');
+ a = this.addition() || entities.keyword() || entities.quoted();
+ if (a) {
+ op = parserInput.$re(/^(?:>=|<=|=<|[<=>])/);
+ if (op) {
+ b = this.addition() || entities.keyword() || entities.quoted();
+ if (b) {
+ c = new(tree.Condition)(op, a, b, index, negate);
+ } else {
+ error('expected expression');
+ }
+ } else {
+ c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
+ }
+ expectChar(')');
+ return parserInput.$re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;
+ }
+ },
+
+ //
+ // An operand is anything that can be part of an operation,
+ // such as a Color, or a Variable
+ //
+ operand: function () {
+ var entities = this.entities, negate;
+
+ if (parserInput.peek(/^-[@\(]/)) {
+ negate = parserInput.$char('-');
+ }
+
+ var o = this.sub() || entities.dimension() ||
+ entities.color() || entities.variable() ||
+ entities.call();
+
+ if (negate) {
+ o.parensInOp = true;
+ o = new(tree.Negative)(o);
+ }
+
+ return o;
+ },
+
+ //
+ // Expressions either represent mathematical operations,
+ // or white-space delimited Entities.
+ //
+ // 1px solid black
+ // @var * 2
+ //
+ expression: function () {
+ var entities = [], e, delim;
+
+ do {
+ e = this.comment();
+ if (e) {
+ entities.push(e);
+ continue;
+ }
+ e = this.addition() || this.entity();
+ if (e) {
+ entities.push(e);
+ // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
+ if (!parserInput.peek(/^\/[\/*]/)) {
+ delim = parserInput.$char('/');
+ if (delim) {
+ entities.push(new(tree.Anonymous)(delim));
+ }
+ }
+ }
+ } while (e);
+ if (entities.length > 0) {
+ return new(tree.Expression)(entities);
+ }
+ },
+ property: function () {
+ var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
+ if (name) {
+ return name[1];
+ }
+ },
+ ruleProperty: function () {
+ var name = [], index = [], s, k;
+
+ parserInput.save();
+
+ function match(re) {
+ var i = parserInput.i,
+ chunk = parserInput.$re(re);
+ if (chunk) {
+ index.push(i);
+ return name.push(chunk[1]);
+ }
+ }
+
+ match(/^(\*?)/);
+ while (true) {
+ if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) {
+ break;
+ }
+ }
+
+ if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) {
+ parserInput.forget();
+
+ // at last, we have the complete match now. move forward,
+ // convert name particles to tree objects and return:
+ if (name[0] === '') {
+ name.shift();
+ index.shift();
+ }
+ for (k = 0; k < name.length; k++) {
+ s = name[k];
+ name[k] = (s.charAt(0) !== '@') ?
+ new(tree.Keyword)(s) :
+ new(tree.Variable)('@' + s.slice(2, -1),
+ index[k], fileInfo);
+ }
+ return name;
+ }
+ parserInput.restore();
+ }
+ }
+ };
+};
+Parser.serializeVars = function(vars) {
+ var s = '';
+
+ for (var name in vars) {
+ if (Object.hasOwnProperty.call(vars, name)) {
+ var value = vars[name];
+ s += ((name[0] === '@') ? '' : '@') + name + ': ' + value +
+ ((String(value).slice(-1) === ';') ? '' : ';');
+ }
+ }
+
+ return s;
+};
+
+module.exports = Parser;
--- /dev/null
+/**
+ * Plugin Manager
+ */
+var PluginManager = function(less) {
+ this.less = less;
+ this.visitors = [];
+ this.preProcessors = [];
+ this.postProcessors = [];
+ this.installedPlugins = [];
+ this.fileManagers = [];
+};
+/**
+ * Adds all the plugins in the array
+ * @param {Array} plugins
+ */
+PluginManager.prototype.addPlugins = function(plugins) {
+ if (plugins) {
+ for (var i = 0; i < plugins.length; i++) {
+ this.addPlugin(plugins[i]);
+ }
+ }
+};
+/**
+ *
+ * @param plugin
+ */
+PluginManager.prototype.addPlugin = function(plugin) {
+ this.installedPlugins.push(plugin);
+ plugin.install(this.less, this);
+};
+/**
+ * Adds a visitor. The visitor object has options on itself to determine
+ * when it should run.
+ * @param visitor
+ */
+PluginManager.prototype.addVisitor = function(visitor) {
+ this.visitors.push(visitor);
+};
+/**
+ * Adds a pre processor object
+ * @param {object} preProcessor
+ * @param {number} priority - guidelines 1 = before import, 1000 = import, 2000 = after import
+ */
+PluginManager.prototype.addPreProcessor = function(preProcessor, priority) {
+ var indexToInsertAt;
+ for (indexToInsertAt = 0; indexToInsertAt < this.preProcessors.length; indexToInsertAt++) {
+ if (this.preProcessors[indexToInsertAt].priority >= priority) {
+ break;
+ }
+ }
+ this.preProcessors.splice(indexToInsertAt, 0, {preProcessor: preProcessor, priority: priority});
+};
+/**
+ * Adds a post processor object
+ * @param {object} postProcessor
+ * @param {number} priority - guidelines 1 = before compression, 1000 = compression, 2000 = after compression
+ */
+PluginManager.prototype.addPostProcessor = function(postProcessor, priority) {
+ var indexToInsertAt;
+ for (indexToInsertAt = 0; indexToInsertAt < this.postProcessors.length; indexToInsertAt++) {
+ if (this.postProcessors[indexToInsertAt].priority >= priority) {
+ break;
+ }
+ }
+ this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority});
+};
+/**
+ *
+ * @param manager
+ */
+PluginManager.prototype.addFileManager = function(manager) {
+ this.fileManagers.push(manager);
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getPreProcessors = function() {
+ var preProcessors = [];
+ for (var i = 0; i < this.preProcessors.length; i++) {
+ preProcessors.push(this.preProcessors[i].preProcessor);
+ }
+ return preProcessors;
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getPostProcessors = function() {
+ var postProcessors = [];
+ for (var i = 0; i < this.postProcessors.length; i++) {
+ postProcessors.push(this.postProcessors[i].postProcessor);
+ }
+ return postProcessors;
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getVisitors = function() {
+ return this.visitors;
+};
+/**
+ *
+ * @returns {Array}
+ * @private
+ */
+PluginManager.prototype.getFileManagers = function() {
+ return this.fileManagers;
+};
+module.exports = PluginManager;
--- /dev/null
+var PromiseConstructor;
+
+module.exports = function(environment, ParseTree, ImportManager) {
+ var render = function (input, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ if (!callback) {
+ if (!PromiseConstructor) {
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
+ }
+ var self = this;
+ return new PromiseConstructor(function (resolve, reject) {
+ render.call(self, input, options, function(err, output) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(output);
+ }
+ });
+ });
+ } else {
+ this.parse(input, options, function(err, root, imports, options) {
+ if (err) { return callback(err); }
+
+ var result;
+ try {
+ var parseTree = new ParseTree(root, imports);
+ result = parseTree.toCSS(options);
+ }
+ catch (err) { return callback(err); }
+
+ callback(null, result);
+ });
+ }
+ };
+
+ return render;
+};
--- /dev/null
+module.exports = function (SourceMapOutput, environment) {
+
+ var SourceMapBuilder = function (options) {
+ this.options = options;
+ };
+
+ SourceMapBuilder.prototype.toCSS = function(rootNode, options, imports) {
+ var sourceMapOutput = new SourceMapOutput(
+ {
+ contentsIgnoredCharsMap: imports.contentsIgnoredChars,
+ rootNode: rootNode,
+ contentsMap: imports.contents,
+ sourceMapFilename: this.options.sourceMapFilename,
+ sourceMapURL: this.options.sourceMapURL,
+ outputFilename: this.options.sourceMapOutputFilename,
+ sourceMapBasepath: this.options.sourceMapBasepath,
+ sourceMapRootpath: this.options.sourceMapRootpath,
+ outputSourceFiles: this.options.outputSourceFiles,
+ sourceMapGenerator: this.options.sourceMapGenerator,
+ sourceMapFileInline: this.options.sourceMapFileInline
+ });
+
+ var css = sourceMapOutput.toCSS(options);
+ this.sourceMap = sourceMapOutput.sourceMap;
+ this.sourceMapURL = sourceMapOutput.sourceMapURL;
+ if (this.options.sourceMapInputFilename) {
+ this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename);
+ }
+ return css + this.getCSSAppendage();
+ };
+
+ SourceMapBuilder.prototype.getCSSAppendage = function() {
+
+ var sourceMapURL = this.sourceMapURL;
+ if (this.options.sourceMapFileInline) {
+ if (this.sourceMap === undefined) {
+ return "";
+ }
+ sourceMapURL = "data:application/json;base64," + environment.encodeBase64(this.sourceMap);
+ }
+
+ if (sourceMapURL) {
+ return "/*# sourceMappingURL=" + sourceMapURL + " */";
+ }
+ return "";
+ };
+
+ SourceMapBuilder.prototype.getExternalSourceMap = function() {
+ return this.sourceMap;
+ };
+ SourceMapBuilder.prototype.setExternalSourceMap = function(sourceMap) {
+ this.sourceMap = sourceMap;
+ };
+
+ SourceMapBuilder.prototype.isInline = function() {
+ return this.options.sourceMapFileInline;
+ };
+ SourceMapBuilder.prototype.getSourceMapURL = function() {
+ return this.sourceMapURL;
+ };
+ SourceMapBuilder.prototype.getOutputFilename = function() {
+ return this.options.sourceMapOutputFilename;
+ };
+ SourceMapBuilder.prototype.getInputFilename = function() {
+ return this.sourceMapInputFilename;
+ };
+
+ return SourceMapBuilder;
+};
--- /dev/null
+module.exports = function (environment) {
+
+ var SourceMapOutput = function (options) {
+ this._css = [];
+ this._rootNode = options.rootNode;
+ this._contentsMap = options.contentsMap;
+ this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap;
+ if (options.sourceMapFilename) {
+ this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/');
+ }
+ this._outputFilename = options.outputFilename;
+ this.sourceMapURL = options.sourceMapURL;
+ if (options.sourceMapBasepath) {
+ this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
+ }
+ if (options.sourceMapRootpath) {
+ this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/');
+ if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') {
+ this._sourceMapRootpath += '/';
+ }
+ } else {
+ this._sourceMapRootpath = "";
+ }
+ this._outputSourceFiles = options.outputSourceFiles;
+ this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator();
+
+ this._lineNumber = 0;
+ this._column = 0;
+ };
+
+ SourceMapOutput.prototype.normalizeFilename = function(filename) {
+ filename = filename.replace(/\\/g, '/');
+
+ if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) {
+ filename = filename.substring(this._sourceMapBasepath.length);
+ if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') {
+ filename = filename.substring(1);
+ }
+ }
+ return (this._sourceMapRootpath || "") + filename;
+ };
+
+ SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
+
+ //ignore adding empty strings
+ if (!chunk) {
+ return;
+ }
+
+ var lines,
+ sourceLines,
+ columns,
+ sourceColumns,
+ i;
+
+ if (fileInfo) {
+ var inputSource = this._contentsMap[fileInfo.filename];
+
+ // remove vars/banner added to the top of the file
+ if (this._contentsIgnoredCharsMap[fileInfo.filename]) {
+ // adjust the index
+ index -= this._contentsIgnoredCharsMap[fileInfo.filename];
+ if (index < 0) { index = 0; }
+ // adjust the source
+ inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]);
+ }
+ inputSource = inputSource.substring(0, index);
+ sourceLines = inputSource.split("\n");
+ sourceColumns = sourceLines[sourceLines.length - 1];
+ }
+
+ lines = chunk.split("\n");
+ columns = lines[lines.length - 1];
+
+ if (fileInfo) {
+ if (!mapLines) {
+ this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column},
+ original: { line: sourceLines.length, column: sourceColumns.length},
+ source: this.normalizeFilename(fileInfo.filename)});
+ } else {
+ for (i = 0; i < lines.length; i++) {
+ this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0},
+ original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0},
+ source: this.normalizeFilename(fileInfo.filename)});
+ }
+ }
+ }
+
+ if (lines.length === 1) {
+ this._column += columns.length;
+ } else {
+ this._lineNumber += lines.length - 1;
+ this._column = columns.length;
+ }
+
+ this._css.push(chunk);
+ };
+
+ SourceMapOutput.prototype.isEmpty = function() {
+ return this._css.length === 0;
+ };
+
+ SourceMapOutput.prototype.toCSS = function(context) {
+ this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
+
+ if (this._outputSourceFiles) {
+ for (var filename in this._contentsMap) {
+ if (this._contentsMap.hasOwnProperty(filename))
+ {
+ var source = this._contentsMap[filename];
+ if (this._contentsIgnoredCharsMap[filename]) {
+ source = source.slice(this._contentsIgnoredCharsMap[filename]);
+ }
+ this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
+ }
+ }
+ }
+
+ this._rootNode.genCSS(context, this);
+
+ if (this._css.length > 0) {
+ var sourceMapURL,
+ sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
+
+ if (this.sourceMapURL) {
+ sourceMapURL = this.sourceMapURL;
+ } else if (this._sourceMapFilename) {
+ sourceMapURL = this._sourceMapFilename;
+ }
+ this.sourceMapURL = sourceMapURL;
+
+ this.sourceMap = sourceMapContent;
+ }
+
+ return this._css.join('');
+ };
+
+ return SourceMapOutput;
+};
--- /dev/null
+var contexts = require("./contexts"),
+ visitor = require("./visitors"),
+ tree = require("./tree");
+
+module.exports = function(root, options) {
+ options = options || {};
+ var evaldRoot,
+ variables = options.variables,
+ evalEnv = new contexts.Eval(options);
+
+ //
+ // Allows setting variables with a hash, so:
+ //
+ // `{ color: new tree.Color('#f01') }` will become:
+ //
+ // new tree.Rule('@color',
+ // new tree.Value([
+ // new tree.Expression([
+ // new tree.Color('#f01')
+ // ])
+ // ])
+ // )
+ //
+ if (typeof variables === 'object' && !Array.isArray(variables)) {
+ variables = Object.keys(variables).map(function (k) {
+ var value = variables[k];
+
+ if (! (value instanceof tree.Value)) {
+ if (! (value instanceof tree.Expression)) {
+ value = new tree.Expression([value]);
+ }
+ value = new tree.Value([value]);
+ }
+ return new tree.Rule('@' + k, value, false, null, 0);
+ });
+ evalEnv.frames = [new tree.Ruleset(null, variables)];
+ }
+
+ var preEvalVisitors = [],
+ visitors = [
+ new visitor.JoinSelectorVisitor(),
+ new visitor.ExtendVisitor(),
+ new visitor.ToCSSVisitor({compress: Boolean(options.compress)})
+ ], i;
+
+ if (options.pluginManager) {
+ var pluginVisitors = options.pluginManager.getVisitors();
+ for (i = 0; i < pluginVisitors.length; i++) {
+ var pluginVisitor = pluginVisitors[i];
+ if (pluginVisitor.isPreEvalVisitor) {
+ preEvalVisitors.push(pluginVisitor);
+ } else {
+ if (pluginVisitor.isPreVisitor) {
+ visitors.splice(0, 0, pluginVisitor);
+ } else {
+ visitors.push(pluginVisitor);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < preEvalVisitors.length; i++) {
+ preEvalVisitors[i].run(root);
+ }
+
+ evaldRoot = root.eval(evalEnv);
+
+ for (i = 0; i < visitors.length; i++) {
+ visitors[i].run(evaldRoot);
+ }
+
+ return evaldRoot;
+};
--- /dev/null
+var Node = require("./node");
+
+var Alpha = function (val) {
+ this.value = val;
+};
+Alpha.prototype = new Node();
+Alpha.prototype.type = "Alpha";
+
+Alpha.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+Alpha.prototype.eval = function (context) {
+ if (this.value.eval) { return new Alpha(this.value.eval(context)); }
+ return this;
+};
+Alpha.prototype.genCSS = function (context, output) {
+ output.add("alpha(opacity=");
+
+ if (this.value.genCSS) {
+ this.value.genCSS(context, output);
+ } else {
+ output.add(this.value);
+ }
+
+ output.add(")");
+};
+
+module.exports = Alpha;
--- /dev/null
+var Node = require("./node");
+
+var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) {
+ this.value = value;
+ this.index = index;
+ this.mapLines = mapLines;
+ this.currentFileInfo = currentFileInfo;
+ this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
+};
+Anonymous.prototype = new Node();
+Anonymous.prototype.type = "Anonymous";
+Anonymous.prototype.eval = function () {
+ return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
+};
+Anonymous.prototype.compare = function (other) {
+ return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
+};
+Anonymous.prototype.isRulesetLike = function() {
+ return this.rulesetLike;
+};
+Anonymous.prototype.genCSS = function (context, output) {
+ output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
+};
+module.exports = Anonymous;
--- /dev/null
+var Node = require("./node");
+
+var Assignment = function (key, val) {
+ this.key = key;
+ this.value = val;
+};
+
+Assignment.prototype = new Node();
+Assignment.prototype.type = "Assignment";
+Assignment.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+Assignment.prototype.eval = function (context) {
+ if (this.value.eval) {
+ return new Assignment(this.key, this.value.eval(context));
+ }
+ return this;
+};
+Assignment.prototype.genCSS = function (context, output) {
+ output.add(this.key + '=');
+ if (this.value.genCSS) {
+ this.value.genCSS(context, output);
+ } else {
+ output.add(this.value);
+ }
+};
+module.exports = Assignment;
--- /dev/null
+var Node = require("./node");
+
+var Attribute = function (key, op, value) {
+ this.key = key;
+ this.op = op;
+ this.value = value;
+};
+Attribute.prototype = new Node();
+Attribute.prototype.type = "Attribute";
+Attribute.prototype.eval = function (context) {
+ return new Attribute(this.key.eval ? this.key.eval(context) : this.key,
+ this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value);
+};
+Attribute.prototype.genCSS = function (context, output) {
+ output.add(this.toCSS(context));
+};
+Attribute.prototype.toCSS = function (context) {
+ var value = this.key.toCSS ? this.key.toCSS(context) : this.key;
+
+ if (this.op) {
+ value += this.op;
+ value += (this.value.toCSS ? this.value.toCSS(context) : this.value);
+ }
+
+ return '[' + value + ']';
+};
+module.exports = Attribute;
--- /dev/null
+var Node = require("./node"),
+ FunctionCaller = require("../functions/function-caller");
+//
+// A function call node.
+//
+var Call = function (name, args, index, currentFileInfo) {
+ this.name = name;
+ this.args = args;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+Call.prototype = new Node();
+Call.prototype.type = "Call";
+Call.prototype.accept = function (visitor) {
+ if (this.args) {
+ this.args = visitor.visitArray(this.args);
+ }
+};
+//
+// When evaluating a function call,
+// we either find the function in the functionRegistry,
+// in which case we call it, passing the evaluated arguments,
+// if this returns null or we cannot find the function, we
+// simply print it out as it appeared originally [2].
+//
+// The reason why we evaluate the arguments, is in the case where
+// we try to pass a variable to a function, like: `saturate(@color)`.
+// The function should receive the value, not the variable.
+//
+Call.prototype.eval = function (context) {
+ var args = this.args.map(function (a) { return a.eval(context); }),
+ result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo);
+
+ if (funcCaller.isValid()) { // 1.
+ try {
+ result = funcCaller.call(args);
+ if (result != null) {
+ return result;
+ }
+ } catch (e) {
+ throw { type: e.type || "Runtime",
+ message: "error evaluating function `" + this.name + "`" +
+ (e.message ? ': ' + e.message : ''),
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+ }
+
+ return new Call(this.name, args, this.index, this.currentFileInfo);
+};
+Call.prototype.genCSS = function (context, output) {
+ output.add(this.name + "(", this.currentFileInfo, this.index);
+
+ for (var i = 0; i < this.args.length; i++) {
+ this.args[i].genCSS(context, output);
+ if (i + 1 < this.args.length) {
+ output.add(", ");
+ }
+ }
+
+ output.add(")");
+};
+module.exports = Call;
--- /dev/null
+var Node = require("./node"),
+ colors = require("../data/colors");
+
+//
+// RGB Colors - #ff0014, #eee
+//
+var Color = function (rgb, a) {
+ //
+ // The end goal here, is to parse the arguments
+ // into an integer triplet, such as `128, 255, 0`
+ //
+ // This facilitates operations and conversions.
+ //
+ if (Array.isArray(rgb)) {
+ this.rgb = rgb;
+ } else if (rgb.length == 6) {
+ this.rgb = rgb.match(/.{2}/g).map(function (c) {
+ return parseInt(c, 16);
+ });
+ } else {
+ this.rgb = rgb.split('').map(function (c) {
+ return parseInt(c + c, 16);
+ });
+ }
+ this.alpha = typeof a === 'number' ? a : 1;
+};
+
+Color.prototype = new Node();
+Color.prototype.type = "Color";
+
+function clamp(v, max) {
+ return Math.min(Math.max(v, 0), max);
+}
+
+function toHex(v) {
+ return '#' + v.map(function (c) {
+ c = clamp(Math.round(c), 255);
+ return (c < 16 ? '0' : '') + c.toString(16);
+ }).join('');
+}
+
+Color.prototype.luma = function () {
+ var r = this.rgb[0] / 255,
+ g = this.rgb[1] / 255,
+ b = this.rgb[2] / 255;
+
+ r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
+ g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
+ b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
+
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+};
+Color.prototype.genCSS = function (context, output) {
+ output.add(this.toCSS(context));
+};
+Color.prototype.toCSS = function (context, doNotCompress) {
+ var compress = context && context.compress && !doNotCompress, color, alpha;
+
+ // `value` is set if this color was originally
+ // converted from a named color string so we need
+ // to respect this and try to output named color too.
+ if (this.value) {
+ return this.value;
+ }
+
+ // If we have some transparency, the only way to represent it
+ // is via `rgba`. Otherwise, we use the hex representation,
+ // which has better compatibility with older browsers.
+ // Values are capped between `0` and `255`, rounded and zero-padded.
+ alpha = this.fround(context, this.alpha);
+ if (alpha < 1) {
+ return "rgba(" + this.rgb.map(function (c) {
+ return clamp(Math.round(c), 255);
+ }).concat(clamp(alpha, 1))
+ .join(',' + (compress ? '' : ' ')) + ")";
+ }
+
+ color = this.toRGB();
+
+ if (compress) {
+ var splitcolor = color.split('');
+
+ // Convert color to short format
+ if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
+ color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
+ }
+ }
+
+ return color;
+};
+
+//
+// Operations have to be done per-channel, if not,
+// channels will spill onto each other. Once we have
+// our result, in the form of an integer triplet,
+// we create a new Color node to hold the result.
+//
+Color.prototype.operate = function (context, op, other) {
+ var rgb = [];
+ var alpha = this.alpha * (1 - other.alpha) + other.alpha;
+ for (var c = 0; c < 3; c++) {
+ rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]);
+ }
+ return new Color(rgb, alpha);
+};
+Color.prototype.toRGB = function () {
+ return toHex(this.rgb);
+};
+Color.prototype.toHSL = function () {
+ var r = this.rgb[0] / 255,
+ g = this.rgb[1] / 255,
+ b = this.rgb[2] / 255,
+ a = this.alpha;
+
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2, d = max - min;
+
+ if (max === min) {
+ h = s = 0;
+ } else {
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+
+ switch (max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+ return { h: h * 360, s: s, l: l, a: a };
+};
+//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+Color.prototype.toHSV = function () {
+ var r = this.rgb[0] / 255,
+ g = this.rgb[1] / 255,
+ b = this.rgb[2] / 255,
+ a = this.alpha;
+
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, v = max;
+
+ var d = max - min;
+ if (max === 0) {
+ s = 0;
+ } else {
+ s = d / max;
+ }
+
+ if (max === min) {
+ h = 0;
+ } else {
+ switch(max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+ return { h: h * 360, s: s, v: v, a: a };
+};
+Color.prototype.toARGB = function () {
+ return toHex([this.alpha * 255].concat(this.rgb));
+};
+Color.prototype.compare = function (x) {
+ return (x.rgb &&
+ x.rgb[0] === this.rgb[0] &&
+ x.rgb[1] === this.rgb[1] &&
+ x.rgb[2] === this.rgb[2] &&
+ x.alpha === this.alpha) ? 0 : undefined;
+};
+
+Color.fromKeyword = function(keyword) {
+ var c, key = keyword.toLowerCase();
+ if (colors.hasOwnProperty(key)) {
+ c = new Color(colors[key].slice(1));
+ }
+ else if (key === "transparent") {
+ c = new Color([0, 0, 0], 0);
+ }
+
+ if (c) {
+ c.value = keyword;
+ return c;
+ }
+};
+module.exports = Color;
--- /dev/null
+var Node = require("./node");
+
+var Combinator = function (value) {
+ if (value === ' ') {
+ this.value = ' ';
+ this.emptyOrWhitespace = true;
+ } else {
+ this.value = value ? value.trim() : "";
+ this.emptyOrWhitespace = this.value === "";
+ }
+};
+Combinator.prototype = new Node();
+Combinator.prototype.type = "Combinator";
+var _noSpaceCombinators = {
+ '': true,
+ ' ': true,
+ '|': true
+};
+Combinator.prototype.genCSS = function (context, output) {
+ var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
+ output.add(spaceOrEmpty + this.value + spaceOrEmpty);
+};
+module.exports = Combinator;
--- /dev/null
+var Node = require("./node"),
+ getDebugInfo = require("./debug-info");
+
+var Comment = function (value, isLineComment, index, currentFileInfo) {
+ this.value = value;
+ this.isLineComment = isLineComment;
+ this.currentFileInfo = currentFileInfo;
+};
+Comment.prototype = new Node();
+Comment.prototype.type = "Comment";
+Comment.prototype.genCSS = function (context, output) {
+ if (this.debugInfo) {
+ output.add(getDebugInfo(context, this), this.currentFileInfo, this.index);
+ }
+ output.add(this.value);
+};
+Comment.prototype.isSilent = function(context) {
+ var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
+ isCompressed = context.compress && this.value[2] !== "!";
+ return this.isLineComment || isReference || isCompressed;
+};
+Comment.prototype.markReferenced = function () {
+ this.isReferenced = true;
+};
+Comment.prototype.isRulesetLike = function(root) {
+ return Boolean(root);
+};
+module.exports = Comment;
--- /dev/null
+var Node = require("./node");
+
+var Condition = function (op, l, r, i, negate) {
+ this.op = op.trim();
+ this.lvalue = l;
+ this.rvalue = r;
+ this.index = i;
+ this.negate = negate;
+};
+Condition.prototype = new Node();
+Condition.prototype.type = "Condition";
+Condition.prototype.accept = function (visitor) {
+ this.lvalue = visitor.visit(this.lvalue);
+ this.rvalue = visitor.visit(this.rvalue);
+};
+Condition.prototype.eval = function (context) {
+ var result = (function (op, a, b) {
+ switch (op) {
+ case 'and': return a && b;
+ case 'or': return a || b;
+ default:
+ switch (Node.compare(a, b)) {
+ case -1:
+ return op === '<' || op === '=<' || op === '<=';
+ case 0:
+ return op === '=' || op === '>=' || op === '=<' || op === '<=';
+ case 1:
+ return op === '>' || op === '>=';
+ default:
+ return false;
+ }
+ }
+ })(this.op, this.lvalue.eval(context), this.rvalue.eval(context));
+
+ return this.negate ? !result : result;
+};
+module.exports = Condition;
--- /dev/null
+var debugInfo = function(context, ctx, lineSeparator) {
+ var result = "";
+ if (context.dumpLineNumbers && !context.compress) {
+ switch(context.dumpLineNumbers) {
+ case 'comments':
+ result = debugInfo.asComment(ctx);
+ break;
+ case 'mediaquery':
+ result = debugInfo.asMediaQuery(ctx);
+ break;
+ case 'all':
+ result = debugInfo.asComment(ctx) + (lineSeparator || "") + debugInfo.asMediaQuery(ctx);
+ break;
+ }
+ }
+ return result;
+};
+
+debugInfo.asComment = function(ctx) {
+ return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
+};
+
+debugInfo.asMediaQuery = function(ctx) {
+ var filenameWithProtocol = ctx.debugInfo.fileName;
+ if (!/^[a-z]+:\/\//i.test(filenameWithProtocol)) {
+ filenameWithProtocol = 'file://' + filenameWithProtocol;
+ }
+ return '@media -sass-debug-info{filename{font-family:' +
+ filenameWithProtocol.replace(/([.:\/\\])/g, function (a) {
+ if (a == '\\') {
+ a = '\/';
+ }
+ return '\\' + a;
+ }) +
+ '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
+};
+
+module.exports = debugInfo;
--- /dev/null
+var Node = require("./node"),
+ contexts = require("../contexts");
+
+var DetachedRuleset = function (ruleset, frames) {
+ this.ruleset = ruleset;
+ this.frames = frames;
+};
+DetachedRuleset.prototype = new Node();
+DetachedRuleset.prototype.type = "DetachedRuleset";
+DetachedRuleset.prototype.evalFirst = true;
+DetachedRuleset.prototype.accept = function (visitor) {
+ this.ruleset = visitor.visit(this.ruleset);
+};
+DetachedRuleset.prototype.eval = function (context) {
+ var frames = this.frames || context.frames.slice(0);
+ return new DetachedRuleset(this.ruleset, frames);
+};
+DetachedRuleset.prototype.callEval = function (context) {
+ return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context);
+};
+module.exports = DetachedRuleset;
--- /dev/null
+var Node = require("./node"),
+ unitConversions = require("../data/unit-conversions"),
+ Unit = require("./unit"),
+ Color = require("./color");
+
+//
+// A number with a unit
+//
+var Dimension = function (value, unit) {
+ this.value = parseFloat(value);
+ this.unit = (unit && unit instanceof Unit) ? unit :
+ new Unit(unit ? [unit] : undefined);
+};
+
+Dimension.prototype = new Node();
+Dimension.prototype.type = "Dimension";
+Dimension.prototype.accept = function (visitor) {
+ this.unit = visitor.visit(this.unit);
+};
+Dimension.prototype.eval = function (context) {
+ return this;
+};
+Dimension.prototype.toColor = function () {
+ return new Color([this.value, this.value, this.value]);
+};
+Dimension.prototype.genCSS = function (context, output) {
+ if ((context && context.strictUnits) && !this.unit.isSingular()) {
+ throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: " + this.unit.toString());
+ }
+
+ var value = this.fround(context, this.value),
+ strValue = String(value);
+
+ if (value !== 0 && value < 0.000001 && value > -0.000001) {
+ // would be output 1e-6 etc.
+ strValue = value.toFixed(20).replace(/0+$/, "");
+ }
+
+ if (context && context.compress) {
+ // Zero values doesn't need a unit
+ if (value === 0 && this.unit.isLength()) {
+ output.add(strValue);
+ return;
+ }
+
+ // Float values doesn't need a leading zero
+ if (value > 0 && value < 1) {
+ strValue = (strValue).substr(1);
+ }
+ }
+
+ output.add(strValue);
+ this.unit.genCSS(context, output);
+};
+
+// In an operation between two Dimensions,
+// we default to the first Dimension's unit,
+// so `1px + 2` will yield `3px`.
+Dimension.prototype.operate = function (context, op, other) {
+ /*jshint noempty:false */
+ var value = this._operate(context, op, this.value, other.value),
+ unit = this.unit.clone();
+
+ if (op === '+' || op === '-') {
+ if (unit.numerator.length === 0 && unit.denominator.length === 0) {
+ unit.numerator = other.unit.numerator.slice(0);
+ unit.denominator = other.unit.denominator.slice(0);
+ } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
+ // do nothing
+ } else {
+ other = other.convertTo(this.unit.usedUnits());
+
+ if (context.strictUnits && other.unit.toString() !== unit.toString()) {
+ throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
+ "' and '" + other.unit.toString() + "'.");
+ }
+
+ value = this._operate(context, op, this.value, other.value);
+ }
+ } else if (op === '*') {
+ unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
+ unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
+ unit.cancel();
+ } else if (op === '/') {
+ unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
+ unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
+ unit.cancel();
+ }
+ return new Dimension(value, unit);
+};
+Dimension.prototype.compare = function (other) {
+ var a, b;
+
+ if (!(other instanceof Dimension)) {
+ return undefined;
+ }
+
+ if (this.unit.isEmpty() || other.unit.isEmpty()) {
+ a = this;
+ b = other;
+ } else {
+ a = this.unify();
+ b = other.unify();
+ if (a.unit.compare(b.unit) !== 0) {
+ return undefined;
+ }
+ }
+
+ return Node.numericCompare(a.value, b.value);
+};
+Dimension.prototype.unify = function () {
+ return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
+};
+Dimension.prototype.convertTo = function (conversions) {
+ var value = this.value, unit = this.unit.clone(),
+ i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
+
+ if (typeof conversions === 'string') {
+ for (i in unitConversions) {
+ if (unitConversions[i].hasOwnProperty(conversions)) {
+ derivedConversions = {};
+ derivedConversions[i] = conversions;
+ }
+ }
+ conversions = derivedConversions;
+ }
+ applyUnit = function (atomicUnit, denominator) {
+ /*jshint loopfunc:true */
+ if (group.hasOwnProperty(atomicUnit)) {
+ if (denominator) {
+ value = value / (group[atomicUnit] / group[targetUnit]);
+ } else {
+ value = value * (group[atomicUnit] / group[targetUnit]);
+ }
+
+ return targetUnit;
+ }
+
+ return atomicUnit;
+ };
+
+ for (groupName in conversions) {
+ if (conversions.hasOwnProperty(groupName)) {
+ targetUnit = conversions[groupName];
+ group = unitConversions[groupName];
+
+ unit.map(applyUnit);
+ }
+ }
+
+ unit.cancel();
+
+ return new Dimension(value, unit);
+};
+module.exports = Dimension;
--- /dev/null
+var Node = require("./node"),
+ Ruleset = require("./ruleset");
+
+var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isReferenced) {
+ this.name = name;
+ this.value = value;
+ if (rules) {
+ this.rules = rules;
+ this.rules.allowImports = true;
+ }
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+ this.debugInfo = debugInfo;
+ this.isReferenced = isReferenced;
+};
+
+Directive.prototype = new Node();
+Directive.prototype.type = "Directive";
+Directive.prototype.accept = function (visitor) {
+ var value = this.value, rules = this.rules;
+ if (rules) {
+ this.rules = visitor.visit(rules);
+ }
+ if (value) {
+ this.value = visitor.visit(value);
+ }
+};
+Directive.prototype.isRulesetLike = function() {
+ return this.rules || !this.isCharset();
+};
+Directive.prototype.isCharset = function() {
+ return "@charset" === this.name;
+};
+Directive.prototype.genCSS = function (context, output) {
+ var value = this.value, rules = this.rules;
+ output.add(this.name, this.currentFileInfo, this.index);
+ if (value) {
+ output.add(' ');
+ value.genCSS(context, output);
+ }
+ if (rules) {
+ if (rules.type === "Ruleset") {
+ rules = [rules];
+ }
+ this.outputRuleset(context, output, rules);
+ } else {
+ output.add(';');
+ }
+};
+Directive.prototype.eval = function (context) {
+ var value = this.value, rules = this.rules;
+ if (value) {
+ value = value.eval(context);
+ }
+ if (rules) {
+ rules = rules.eval(context);
+ rules.root = true;
+ }
+ return new Directive(this.name, value, rules,
+ this.index, this.currentFileInfo, this.debugInfo, this.isReferenced);
+};
+Directive.prototype.variable = function (name) {
+ if (this.rules) {
+ return Ruleset.prototype.variable.call(this.rules, name);
+ }
+};
+Directive.prototype.find = function () {
+ if (this.rules) {
+ return Ruleset.prototype.find.apply(this.rules, arguments);
+ }
+};
+Directive.prototype.rulesets = function () {
+ if (this.rules) {
+ return Ruleset.prototype.rulesets.apply(this.rules);
+ }
+};
+Directive.prototype.markReferenced = function () {
+ var i, rules;
+ this.isReferenced = true;
+ if (this.rules) {
+ rules = this.rules.rules;
+ for (i = 0; i < rules.length; i++) {
+ if (rules[i].markReferenced) {
+ rules[i].markReferenced();
+ }
+ }
+ }
+};
+Directive.prototype.getIsReferenced = function () {
+ return !this.currentFileInfo || !this.currentFileInfo.reference || this.isReferenced;
+};
+Directive.prototype.outputRuleset = function (context, output, rules) {
+ var ruleCnt = rules.length, i;
+ context.tabLevel = (context.tabLevel | 0) + 1;
+
+ // Compressed
+ if (context.compress) {
+ output.add('{');
+ for (i = 0; i < ruleCnt; i++) {
+ rules[i].genCSS(context, output);
+ }
+ output.add('}');
+ context.tabLevel--;
+ return;
+ }
+
+ // Non-compressed
+ var tabSetStr = '\n' + Array(context.tabLevel).join(" "), tabRuleStr = tabSetStr + " ";
+ if (!ruleCnt) {
+ output.add(" {" + tabSetStr + '}');
+ } else {
+ output.add(" {" + tabRuleStr);
+ rules[0].genCSS(context, output);
+ for (i = 1; i < ruleCnt; i++) {
+ output.add(tabRuleStr);
+ rules[i].genCSS(context, output);
+ }
+ output.add(tabSetStr + '}');
+ }
+
+ context.tabLevel--;
+};
+module.exports = Directive;
--- /dev/null
+var Node = require("./node"),
+ Paren = require("./paren"),
+ Combinator = require("./combinator");
+
+var Element = function (combinator, value, index, currentFileInfo) {
+ this.combinator = combinator instanceof Combinator ?
+ combinator : new Combinator(combinator);
+
+ if (typeof value === 'string') {
+ this.value = value.trim();
+ } else if (value) {
+ this.value = value;
+ } else {
+ this.value = "";
+ }
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+Element.prototype = new Node();
+Element.prototype.type = "Element";
+Element.prototype.accept = function (visitor) {
+ var value = this.value;
+ this.combinator = visitor.visit(this.combinator);
+ if (typeof value === "object") {
+ this.value = visitor.visit(value);
+ }
+};
+Element.prototype.eval = function (context) {
+ return new Element(this.combinator,
+ this.value.eval ? this.value.eval(context) : this.value,
+ this.index,
+ this.currentFileInfo);
+};
+Element.prototype.genCSS = function (context, output) {
+ output.add(this.toCSS(context), this.currentFileInfo, this.index);
+};
+Element.prototype.toCSS = function (context) {
+ context = context || {};
+ var value = this.value, firstSelector = context.firstSelector;
+ if (value instanceof Paren) {
+ // selector in parens should not be affected by outer selector
+ // flags (breaks only interpolated selectors - see #1973)
+ context.firstSelector = true;
+ }
+ value = value.toCSS ? value.toCSS(context) : value;
+ context.firstSelector = firstSelector;
+ if (value === '' && this.combinator.value.charAt(0) === '&') {
+ return '';
+ } else {
+ return this.combinator.toCSS(context) + value;
+ }
+};
+module.exports = Element;
--- /dev/null
+var Node = require("./node"),
+ Paren = require("./paren"),
+ Comment = require("./comment");
+
+var Expression = function (value) {
+ this.value = value;
+ if (!value) {
+ throw new Error("Expression requires an array parameter");
+ }
+};
+Expression.prototype = new Node();
+Expression.prototype.type = "Expression";
+Expression.prototype.accept = function (visitor) {
+ this.value = visitor.visitArray(this.value);
+};
+Expression.prototype.eval = function (context) {
+ var returnValue,
+ inParenthesis = this.parens && !this.parensInOp,
+ doubleParen = false;
+ if (inParenthesis) {
+ context.inParenthesis();
+ }
+ if (this.value.length > 1) {
+ returnValue = new Expression(this.value.map(function (e) {
+ return e.eval(context);
+ }));
+ } else if (this.value.length === 1) {
+ if (this.value[0].parens && !this.value[0].parensInOp) {
+ doubleParen = true;
+ }
+ returnValue = this.value[0].eval(context);
+ } else {
+ returnValue = this;
+ }
+ if (inParenthesis) {
+ context.outOfParenthesis();
+ }
+ if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) {
+ returnValue = new Paren(returnValue);
+ }
+ return returnValue;
+};
+Expression.prototype.genCSS = function (context, output) {
+ for (var i = 0; i < this.value.length; i++) {
+ this.value[i].genCSS(context, output);
+ if (i + 1 < this.value.length) {
+ output.add(" ");
+ }
+ }
+};
+Expression.prototype.throwAwayComments = function () {
+ this.value = this.value.filter(function(v) {
+ return !(v instanceof Comment);
+ });
+};
+module.exports = Expression;
--- /dev/null
+var Node = require("./node");
+
+var Extend = function Extend(selector, option, index) {
+ this.selector = selector;
+ this.option = option;
+ this.index = index;
+ this.object_id = Extend.next_id++;
+ this.parent_ids = [this.object_id];
+
+ switch(option) {
+ case "all":
+ this.allowBefore = true;
+ this.allowAfter = true;
+ break;
+ default:
+ this.allowBefore = false;
+ this.allowAfter = false;
+ break;
+ }
+};
+Extend.next_id = 0;
+
+Extend.prototype = new Node();
+Extend.prototype.type = "Extend";
+Extend.prototype.accept = function (visitor) {
+ this.selector = visitor.visit(this.selector);
+};
+Extend.prototype.eval = function (context) {
+ return new Extend(this.selector.eval(context), this.option, this.index);
+};
+Extend.prototype.clone = function (context) {
+ return new Extend(this.selector, this.option, this.index);
+};
+Extend.prototype.findSelfSelectors = function (selectors) {
+ var selfElements = [],
+ i,
+ selectorElements;
+
+ for (i = 0; i < selectors.length; i++) {
+ selectorElements = selectors[i].elements;
+ // duplicate the logic in genCSS function inside the selector node.
+ // future TODO - move both logics into the selector joiner visitor
+ if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
+ selectorElements[0].combinator.value = ' ';
+ }
+ selfElements = selfElements.concat(selectors[i].elements);
+ }
+
+ this.selfSelectors = [{ elements: selfElements }];
+};
+module.exports = Extend;
--- /dev/null
+var Node = require("./node"),
+ Media = require("./media"),
+ URL = require("./url"),
+ Quoted = require("./quoted"),
+ Ruleset = require("./ruleset"),
+ Anonymous = require("./anonymous");
+
+//
+// CSS @import node
+//
+// The general strategy here is that we don't want to wait
+// for the parsing to be completed, before we start importing
+// the file. That's because in the context of a browser,
+// most of the time will be spent waiting for the server to respond.
+//
+// On creation, we push the import path to our import queue, though
+// `import,push`, we also pass it a callback, which it'll call once
+// the file has been fetched, and parsed.
+//
+var Import = function (path, features, options, index, currentFileInfo) {
+ this.options = options;
+ this.index = index;
+ this.path = path;
+ this.features = features;
+ this.currentFileInfo = currentFileInfo;
+
+ if (this.options.less !== undefined || this.options.inline) {
+ this.css = !this.options.less || this.options.inline;
+ } else {
+ var pathValue = this.getPath();
+ if (pathValue && /[#\.\&\?\/]css([\?;].*)?$/.test(pathValue)) {
+ this.css = true;
+ }
+ }
+};
+
+//
+// The actual import node doesn't return anything, when converted to CSS.
+// The reason is that it's used at the evaluation stage, so that the rules
+// it imports can be treated like any other rules.
+//
+// In `eval`, we make sure all Import nodes get evaluated, recursively, so
+// we end up with a flat structure, which can easily be imported in the parent
+// ruleset.
+//
+Import.prototype = new Node();
+Import.prototype.type = "Import";
+Import.prototype.accept = function (visitor) {
+ if (this.features) {
+ this.features = visitor.visit(this.features);
+ }
+ this.path = visitor.visit(this.path);
+ if (!this.options.inline && this.root) {
+ this.root = visitor.visit(this.root);
+ }
+};
+Import.prototype.genCSS = function (context, output) {
+ if (this.css) {
+ output.add("@import ", this.currentFileInfo, this.index);
+ this.path.genCSS(context, output);
+ if (this.features) {
+ output.add(" ");
+ this.features.genCSS(context, output);
+ }
+ output.add(';');
+ }
+};
+Import.prototype.getPath = function () {
+ if (this.path instanceof Quoted) {
+ return this.path.value;
+ } else if (this.path instanceof URL) {
+ return this.path.value.value;
+ }
+ return null;
+};
+Import.prototype.isVariableImport = function () {
+ var path = this.path;
+ if (path instanceof URL) {
+ path = path.value;
+ }
+ if (path instanceof Quoted) {
+ return path.containsVariables();
+ }
+
+ return true;
+};
+Import.prototype.evalForImport = function (context) {
+ var path = this.path;
+ if (path instanceof URL) {
+ path = path.value;
+ }
+ return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo);
+};
+Import.prototype.evalPath = function (context) {
+ var path = this.path.eval(context);
+ var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
+
+ if (!(path instanceof URL)) {
+ if (rootpath) {
+ var pathValue = path.value;
+ // Add the base path if the import is relative
+ if (pathValue && context.isPathRelative(pathValue)) {
+ path.value = rootpath + pathValue;
+ }
+ }
+ path.value = context.normalizePath(path.value);
+ }
+
+ return path;
+};
+Import.prototype.eval = function (context) {
+ var ruleset, features = this.features && this.features.eval(context);
+
+ if (this.skip) {
+ if (typeof this.skip === "function") {
+ this.skip = this.skip();
+ }
+ if (this.skip) {
+ return [];
+ }
+ }
+
+ if (this.options.inline) {
+ var contents = new Anonymous(this.root, 0, {filename: this.importedFilename}, true, true);
+ return this.features ? new Media([contents], this.features.value) : [contents];
+ } else if (this.css) {
+ var newImport = new Import(this.evalPath(context), features, this.options, this.index);
+ if (!newImport.css && this.error) {
+ throw this.error;
+ }
+ return newImport;
+ } else {
+ ruleset = new Ruleset(null, this.root.rules.slice(0));
+
+ ruleset.evalImports(context);
+
+ return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules;
+ }
+};
+module.exports = Import;
--- /dev/null
+var tree = {};
+
+tree.Node = require('./node');
+tree.Alpha = require('./alpha');
+tree.Color = require('./color');
+tree.Directive = require('./directive');
+tree.DetachedRuleset = require('./detached-ruleset');
+tree.Operation = require('./operation');
+tree.Dimension = require('./dimension');
+tree.Unit = require('./unit');
+tree.Keyword = require('./keyword');
+tree.Variable = require('./variable');
+tree.Ruleset = require('./ruleset');
+tree.Element = require('./element');
+tree.Attribute = require('./attribute');
+tree.Combinator = require('./combinator');
+tree.Selector = require('./selector');
+tree.Quoted = require('./quoted');
+tree.Expression = require('./expression');
+tree.Rule = require('./rule');
+tree.Call = require('./call');
+tree.URL = require('./url');
+tree.Import = require('./import');
+tree.mixin = {
+ Call: require('./mixin-call'),
+ Definition: require('./mixin-definition')
+};
+tree.Comment = require('./comment');
+tree.Anonymous = require('./anonymous');
+tree.Value = require('./value');
+tree.JavaScript = require('./javascript');
+tree.Assignment = require('./assignment');
+tree.Condition = require('./condition');
+tree.Paren = require('./paren');
+tree.Media = require('./media');
+tree.UnicodeDescriptor = require('./unicode-descriptor');
+tree.Negative = require('./negative');
+tree.Extend = require('./extend');
+tree.RulesetCall = require('./ruleset-call');
+
+module.exports = tree;
--- /dev/null
+var JsEvalNode = require("./js-eval-node"),
+ Dimension = require("./dimension"),
+ Quoted = require("./quoted"),
+ Anonymous = require("./anonymous");
+
+var JavaScript = function (string, escaped, index, currentFileInfo) {
+ this.escaped = escaped;
+ this.expression = string;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+JavaScript.prototype = new JsEvalNode();
+JavaScript.prototype.type = "JavaScript";
+JavaScript.prototype.eval = function(context) {
+ var result = this.evaluateJavaScript(this.expression, context);
+
+ if (typeof result === 'number') {
+ return new Dimension(result);
+ } else if (typeof result === 'string') {
+ return new Quoted('"' + result + '"', result, this.escaped, this.index);
+ } else if (Array.isArray(result)) {
+ return new Anonymous(result.join(', '));
+ } else {
+ return new Anonymous(result);
+ }
+};
+
+module.exports = JavaScript;
--- /dev/null
+var Node = require("./node"),
+ Variable = require("./variable");
+
+var JsEvalNode = function() {
+};
+JsEvalNode.prototype = new Node();
+
+JsEvalNode.prototype.evaluateJavaScript = function (expression, context) {
+ var result,
+ that = this,
+ evalContext = {};
+
+ if (context.javascriptEnabled !== undefined && !context.javascriptEnabled) {
+ throw { message: "You are using JavaScript, which has been disabled.",
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+
+ expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
+ return that.jsify(new Variable('@' + name, that.index, that.currentFileInfo).eval(context));
+ });
+
+ try {
+ expression = new Function('return (' + expression + ')');
+ } catch (e) {
+ throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+
+ var variables = context.frames[0].variables();
+ for (var k in variables) {
+ if (variables.hasOwnProperty(k)) {
+ /*jshint loopfunc:true */
+ evalContext[k.slice(1)] = {
+ value: variables[k].value,
+ toJS: function () {
+ return this.value.eval(context).toCSS();
+ }
+ };
+ }
+ }
+
+ try {
+ result = expression.call(evalContext);
+ } catch (e) {
+ throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+ return result;
+};
+JsEvalNode.prototype.jsify = function (obj) {
+ if (Array.isArray(obj.value) && (obj.value.length > 1)) {
+ return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
+ } else {
+ return obj.toCSS();
+ }
+};
+
+module.exports = JsEvalNode;
--- /dev/null
+var Node = require("./node");
+
+var Keyword = function (value) { this.value = value; };
+Keyword.prototype = new Node();
+Keyword.prototype.type = "Keyword";
+Keyword.prototype.genCSS = function (context, output) {
+ if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
+ output.add(this.value);
+};
+
+Keyword.True = new Keyword('true');
+Keyword.False = new Keyword('false');
+
+module.exports = Keyword;
--- /dev/null
+var Ruleset = require("./ruleset"),
+ Value = require("./value"),
+ Element = require("./element"),
+ Selector = require("./selector"),
+ Anonymous = require("./anonymous"),
+ Expression = require("./expression"),
+ Directive = require("./directive");
+
+var Media = function (value, features, index, currentFileInfo) {
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+
+ var selectors = this.emptySelectors();
+
+ this.features = new Value(features);
+ this.rules = [new Ruleset(selectors, value)];
+ this.rules[0].allowImports = true;
+};
+Media.prototype = new Directive();
+Media.prototype.type = "Media";
+Media.prototype.isRulesetLike = true;
+Media.prototype.accept = function (visitor) {
+ if (this.features) {
+ this.features = visitor.visit(this.features);
+ }
+ if (this.rules) {
+ this.rules = visitor.visitArray(this.rules);
+ }
+};
+Media.prototype.genCSS = function (context, output) {
+ output.add('@media ', this.currentFileInfo, this.index);
+ this.features.genCSS(context, output);
+ this.outputRuleset(context, output, this.rules);
+};
+Media.prototype.eval = function (context) {
+ if (!context.mediaBlocks) {
+ context.mediaBlocks = [];
+ context.mediaPath = [];
+ }
+
+ var media = new Media(null, [], this.index, this.currentFileInfo);
+ if (this.debugInfo) {
+ this.rules[0].debugInfo = this.debugInfo;
+ media.debugInfo = this.debugInfo;
+ }
+ var strictMathBypass = false;
+ if (!context.strictMath) {
+ strictMathBypass = true;
+ context.strictMath = true;
+ }
+ try {
+ media.features = this.features.eval(context);
+ }
+ finally {
+ if (strictMathBypass) {
+ context.strictMath = false;
+ }
+ }
+
+ context.mediaPath.push(media);
+ context.mediaBlocks.push(media);
+
+ context.frames.unshift(this.rules[0]);
+ media.rules = [this.rules[0].eval(context)];
+ context.frames.shift();
+
+ context.mediaPath.pop();
+
+ return context.mediaPath.length === 0 ? media.evalTop(context) :
+ media.evalNested(context);
+};
+//TODO merge with directive
+Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); };
+Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); };
+Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); };
+Media.prototype.emptySelectors = function() {
+ var el = new Element('', '&', this.index, this.currentFileInfo),
+ sels = [new Selector([el], null, null, this.index, this.currentFileInfo)];
+ sels[0].mediaEmpty = true;
+ return sels;
+};
+Media.prototype.markReferenced = function () {
+ var i, rules = this.rules[0].rules;
+ this.rules[0].markReferenced();
+ this.isReferenced = true;
+ for (i = 0; i < rules.length; i++) {
+ if (rules[i].markReferenced) {
+ rules[i].markReferenced();
+ }
+ }
+};
+Media.prototype.evalTop = function (context) {
+ var result = this;
+
+ // Render all dependent Media blocks.
+ if (context.mediaBlocks.length > 1) {
+ var selectors = this.emptySelectors();
+ result = new Ruleset(selectors, context.mediaBlocks);
+ result.multiMedia = true;
+ }
+
+ delete context.mediaBlocks;
+ delete context.mediaPath;
+
+ return result;
+};
+Media.prototype.evalNested = function (context) {
+ var i, value,
+ path = context.mediaPath.concat([this]);
+
+ // Extract the media-query conditions separated with `,` (OR).
+ for (i = 0; i < path.length; i++) {
+ value = path[i].features instanceof Value ?
+ path[i].features.value : path[i].features;
+ path[i] = Array.isArray(value) ? value : [value];
+ }
+
+ // Trace all permutations to generate the resulting media-query.
+ //
+ // (a, b and c) with nested (d, e) ->
+ // a and d
+ // a and e
+ // b and c and d
+ // b and c and e
+ this.features = new Value(this.permute(path).map(function (path) {
+ path = path.map(function (fragment) {
+ return fragment.toCSS ? fragment : new Anonymous(fragment);
+ });
+
+ for (i = path.length - 1; i > 0; i--) {
+ path.splice(i, 0, new Anonymous("and"));
+ }
+
+ return new Expression(path);
+ }));
+
+ // Fake a tree-node that doesn't output anything.
+ return new Ruleset([], []);
+};
+Media.prototype.permute = function (arr) {
+ if (arr.length === 0) {
+ return [];
+ } else if (arr.length === 1) {
+ return arr[0];
+ } else {
+ var result = [];
+ var rest = this.permute(arr.slice(1));
+ for (var i = 0; i < rest.length; i++) {
+ for (var j = 0; j < arr[0].length; j++) {
+ result.push([arr[0][j]].concat(rest[i]));
+ }
+ }
+ return result;
+ }
+};
+Media.prototype.bubbleSelectors = function (selectors) {
+ if (!selectors) {
+ return;
+ }
+ this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])];
+};
+module.exports = Media;
--- /dev/null
+var Node = require("./node"),
+ Selector = require("./selector"),
+ MixinDefinition = require("./mixin-definition"),
+ defaultFunc = require("../functions/default");
+
+var MixinCall = function (elements, args, index, currentFileInfo, important) {
+ this.selector = new Selector(elements);
+ this.arguments = (args && args.length) ? args : null;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+ this.important = important;
+};
+MixinCall.prototype = new Node();
+MixinCall.prototype.type = "MixinCall";
+MixinCall.prototype.accept = function (visitor) {
+ if (this.selector) {
+ this.selector = visitor.visit(this.selector);
+ }
+ if (this.arguments) {
+ this.arguments = visitor.visitArray(this.arguments);
+ }
+};
+MixinCall.prototype.eval = function (context) {
+ var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
+ candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase = -1,
+ defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter;
+
+ function calcDefGroup(mixin, mixinPath) {
+ var p, namespace;
+
+ for (f = 0; f < 2; f++) {
+ conditionResult[f] = true;
+ defaultFunc.value(f);
+ for (p = 0; p < mixinPath.length && conditionResult[f]; p++) {
+ namespace = mixinPath[p];
+ if (namespace.matchCondition) {
+ conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context);
+ }
+ }
+ if (mixin.matchCondition) {
+ conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context);
+ }
+ }
+ if (conditionResult[0] || conditionResult[1]) {
+ if (conditionResult[0] != conditionResult[1]) {
+ return conditionResult[1] ?
+ defTrue : defFalse;
+ }
+
+ return defNone;
+ }
+ return defFalseEitherCase;
+ }
+
+ args = this.arguments && this.arguments.map(function (a) {
+ return { name: a.name, value: a.value.eval(context) };
+ });
+
+ noArgumentsFilter = function(rule) {return rule.matchArgs(null, context);};
+
+ for (i = 0; i < context.frames.length; i++) {
+ if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) {
+ isOneFound = true;
+
+ // To make `default()` function independent of definition order we have two "subpasses" here.
+ // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
+ // and build candidate list with corresponding flags. Then, when we know all possible matches,
+ // we make a final decision.
+
+ for (m = 0; m < mixins.length; m++) {
+ mixin = mixins[m].rule;
+ mixinPath = mixins[m].path;
+ isRecursive = false;
+ for (f = 0; f < context.frames.length; f++) {
+ if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) {
+ isRecursive = true;
+ break;
+ }
+ }
+ if (isRecursive) {
+ continue;
+ }
+
+ if (mixin.matchArgs(args, context)) {
+ candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)};
+
+ if (candidate.group !== defFalseEitherCase) {
+ candidates.push(candidate);
+ }
+
+ match = true;
+ }
+ }
+
+ defaultFunc.reset();
+
+ count = [0, 0, 0];
+ for (m = 0; m < candidates.length; m++) {
+ count[candidates[m].group]++;
+ }
+
+ if (count[defNone] > 0) {
+ defaultResult = defFalse;
+ } else {
+ defaultResult = defTrue;
+ if ((count[defTrue] + count[defFalse]) > 1) {
+ throw { type: 'Runtime',
+ message: 'Ambiguous use of `default()` found when matching for `' + this.format(args) + '`',
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+ }
+
+ for (m = 0; m < candidates.length; m++) {
+ candidate = candidates[m].group;
+ if ((candidate === defNone) || (candidate === defaultResult)) {
+ try {
+ mixin = candidates[m].mixin;
+ if (!(mixin instanceof MixinDefinition)) {
+ originalRuleset = mixin.originalRuleset || mixin;
+ mixin = new MixinDefinition("", [], mixin.rules, null, false);
+ mixin.originalRuleset = originalRuleset;
+ }
+ Array.prototype.push.apply(
+ rules, mixin.evalCall(context, args, this.important).rules);
+ } catch (e) {
+ throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
+ }
+ }
+ }
+
+ if (match) {
+ if (!this.currentFileInfo || !this.currentFileInfo.reference) {
+ for (i = 0; i < rules.length; i++) {
+ rule = rules[i];
+ if (rule.markReferenced) {
+ rule.markReferenced();
+ }
+ }
+ }
+ return rules;
+ }
+ }
+ }
+ if (isOneFound) {
+ throw { type: 'Runtime',
+ message: 'No matching definition was found for `' + this.format(args) + '`',
+ index: this.index, filename: this.currentFileInfo.filename };
+ } else {
+ throw { type: 'Name',
+ message: this.selector.toCSS().trim() + " is undefined",
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+};
+MixinCall.prototype.format = function (args) {
+ return this.selector.toCSS().trim() + '(' +
+ (args ? args.map(function (a) {
+ var argValue = "";
+ if (a.name) {
+ argValue += a.name + ":";
+ }
+ if (a.value.toCSS) {
+ argValue += a.value.toCSS();
+ } else {
+ argValue += "???";
+ }
+ return argValue;
+ }).join(', ') : "") + ")";
+};
+module.exports = MixinCall;
--- /dev/null
+var Selector = require("./selector"),
+ Element = require("./element"),
+ Ruleset = require("./ruleset"),
+ Rule = require("./rule"),
+ Expression = require("./expression"),
+ contexts = require("../contexts");
+
+var Definition = function (name, params, rules, condition, variadic, frames) {
+ this.name = name;
+ this.selectors = [new Selector([new Element(null, name, this.index, this.currentFileInfo)])];
+ this.params = params;
+ this.condition = condition;
+ this.variadic = variadic;
+ this.arity = params.length;
+ this.rules = rules;
+ this._lookups = {};
+ this.required = params.reduce(function (count, p) {
+ if (!p.name || (p.name && !p.value)) {
+ return count + 1;
+ }
+ else {
+ return count;
+ }
+ }, 0);
+ this.frames = frames;
+};
+Definition.prototype = new Ruleset();
+Definition.prototype.type = "MixinDefinition";
+Definition.prototype.evalFirst = true;
+Definition.prototype.accept = function (visitor) {
+ if (this.params && this.params.length) {
+ this.params = visitor.visitArray(this.params);
+ }
+ this.rules = visitor.visitArray(this.rules);
+ if (this.condition) {
+ this.condition = visitor.visit(this.condition);
+ }
+};
+Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) {
+ /*jshint boss:true */
+ var frame = new Ruleset(null, null),
+ varargs, arg,
+ params = this.params.slice(0),
+ i, j, val, name, isNamedFound, argIndex, argsLength = 0;
+
+ mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
+
+ if (args) {
+ args = args.slice(0);
+ argsLength = args.length;
+
+ for (i = 0; i < argsLength; i++) {
+ arg = args[i];
+ if (name = (arg && arg.name)) {
+ isNamedFound = false;
+ for (j = 0; j < params.length; j++) {
+ if (!evaldArguments[j] && name === params[j].name) {
+ evaldArguments[j] = arg.value.eval(context);
+ frame.prependRule(new Rule(name, arg.value.eval(context)));
+ isNamedFound = true;
+ break;
+ }
+ }
+ if (isNamedFound) {
+ args.splice(i, 1);
+ i--;
+ continue;
+ } else {
+ throw { type: 'Runtime', message: "Named argument for " + this.name +
+ ' ' + args[i].name + ' not found' };
+ }
+ }
+ }
+ }
+ argIndex = 0;
+ for (i = 0; i < params.length; i++) {
+ if (evaldArguments[i]) { continue; }
+
+ arg = args && args[argIndex];
+
+ if (name = params[i].name) {
+ if (params[i].variadic) {
+ varargs = [];
+ for (j = argIndex; j < argsLength; j++) {
+ varargs.push(args[j].value.eval(context));
+ }
+ frame.prependRule(new Rule(name, new Expression(varargs).eval(context)));
+ } else {
+ val = arg && arg.value;
+ if (val) {
+ val = val.eval(context);
+ } else if (params[i].value) {
+ val = params[i].value.eval(mixinEnv);
+ frame.resetCache();
+ } else {
+ throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
+ ' (' + argsLength + ' for ' + this.arity + ')' };
+ }
+
+ frame.prependRule(new Rule(name, val));
+ evaldArguments[i] = val;
+ }
+ }
+
+ if (params[i].variadic && args) {
+ for (j = argIndex; j < argsLength; j++) {
+ evaldArguments[j] = args[j].value.eval(context);
+ }
+ }
+ argIndex++;
+ }
+
+ return frame;
+};
+Definition.prototype.makeImportant = function() {
+ var rules = !this.rules ? this.rules : this.rules.map(function (r) {
+ if (r.makeImportant) {
+ return r.makeImportant(true);
+ } else {
+ return r;
+ }
+ });
+ var result = new Definition (this.name, this.params, rules, this.condition, this.variadic, this.frames);
+ return result;
+};
+Definition.prototype.eval = function (context) {
+ return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || context.frames.slice(0));
+};
+Definition.prototype.evalCall = function (context, args, important) {
+ var _arguments = [],
+ mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames,
+ frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments),
+ rules, ruleset;
+
+ frame.prependRule(new Rule('@arguments', new Expression(_arguments).eval(context)));
+
+ rules = this.rules.slice(0);
+
+ ruleset = new Ruleset(null, rules);
+ ruleset.originalRuleset = this;
+ ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames)));
+ if (important) {
+ ruleset = ruleset.makeImportant();
+ }
+ return ruleset;
+};
+Definition.prototype.matchCondition = function (args, context) {
+ if (this.condition && !this.condition.eval(
+ new contexts.Eval(context,
+ [this.evalParams(context, /* the parameter variables*/
+ new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
+ .concat(this.frames) // the parent namespace/mixin frames
+ .concat(context.frames)))) { // the current environment frames
+ return false;
+ }
+ return true;
+};
+Definition.prototype.matchArgs = function (args, context) {
+ var argsLength = (args && args.length) || 0, len;
+
+ if (! this.variadic) {
+ if (argsLength < this.required) {
+ return false;
+ }
+ if (argsLength > this.params.length) {
+ return false;
+ }
+ } else {
+ if (argsLength < (this.required - 1)) {
+ return false;
+ }
+ }
+
+ len = Math.min(argsLength, this.arity);
+
+ for (var i = 0; i < len; i++) {
+ if (!this.params[i].name && !this.params[i].variadic) {
+ if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) {
+ return false;
+ }
+ }
+ }
+ return true;
+};
+module.exports = Definition;
--- /dev/null
+var Node = require("./node"),
+ Operation = require("./operation"),
+ Dimension = require("./dimension");
+
+var Negative = function (node) {
+ this.value = node;
+};
+Negative.prototype = new Node();
+Negative.prototype.type = "Negative";
+Negative.prototype.genCSS = function (context, output) {
+ output.add('-');
+ this.value.genCSS(context, output);
+};
+Negative.prototype.eval = function (context) {
+ if (context.isMathOn()) {
+ return (new Operation('*', [new Dimension(-1), this.value])).eval(context);
+ }
+ return new Negative(this.value.eval(context));
+};
+module.exports = Negative;
--- /dev/null
+var Node = function() {
+};
+Node.prototype.toCSS = function (context) {
+ var strs = [];
+ this.genCSS(context, {
+ add: function(chunk, fileInfo, index) {
+ strs.push(chunk);
+ },
+ isEmpty: function () {
+ return strs.length === 0;
+ }
+ });
+ return strs.join('');
+};
+Node.prototype.genCSS = function (context, output) {
+ output.add(this.value);
+};
+Node.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+Node.prototype.eval = function () { return this; };
+Node.prototype._operate = function (context, op, a, b) {
+ switch (op) {
+ case '+': return a + b;
+ case '-': return a - b;
+ case '*': return a * b;
+ case '/': return a / b;
+ }
+};
+Node.prototype.fround = function(context, value) {
+ var precision = context && context.numPrecision;
+ //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
+ return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
+};
+Node.compare = function (a, b) {
+ /* returns:
+ -1: a < b
+ 0: a = b
+ 1: a > b
+ and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */
+
+ if ((a.compare) &&
+ // for "symmetric results" force toCSS-based comparison
+ // of Quoted or Anonymous if either value is one of those
+ !(b.type === "Quoted" || b.type === "Anonymous")) {
+ return a.compare(b);
+ } else if (b.compare) {
+ return -b.compare(a);
+ } else if (a.type !== b.type) {
+ return undefined;
+ }
+
+ a = a.value;
+ b = b.value;
+ if (!Array.isArray(a)) {
+ return a === b ? 0 : undefined;
+ }
+ if (a.length !== b.length) {
+ return undefined;
+ }
+ for (var i = 0; i < a.length; i++) {
+ if (Node.compare(a[i], b[i]) !== 0) {
+ return undefined;
+ }
+ }
+ return 0;
+};
+
+Node.numericCompare = function (a, b) {
+ return a < b ? -1
+ : a === b ? 0
+ : a > b ? 1 : undefined;
+};
+module.exports = Node;
--- /dev/null
+var Node = require("./node"),
+ Color = require("./color"),
+ Dimension = require("./dimension");
+
+var Operation = function (op, operands, isSpaced) {
+ this.op = op.trim();
+ this.operands = operands;
+ this.isSpaced = isSpaced;
+};
+Operation.prototype = new Node();
+Operation.prototype.type = "Operation";
+Operation.prototype.accept = function (visitor) {
+ this.operands = visitor.visit(this.operands);
+};
+Operation.prototype.eval = function (context) {
+ var a = this.operands[0].eval(context),
+ b = this.operands[1].eval(context);
+
+ if (context.isMathOn()) {
+ if (a instanceof Dimension && b instanceof Color) {
+ a = a.toColor();
+ }
+ if (b instanceof Dimension && a instanceof Color) {
+ b = b.toColor();
+ }
+ if (!a.operate) {
+ throw { type: "Operation",
+ message: "Operation on an invalid type" };
+ }
+
+ return a.operate(context, this.op, b);
+ } else {
+ return new Operation(this.op, [a, b], this.isSpaced);
+ }
+};
+Operation.prototype.genCSS = function (context, output) {
+ this.operands[0].genCSS(context, output);
+ if (this.isSpaced) {
+ output.add(" ");
+ }
+ output.add(this.op);
+ if (this.isSpaced) {
+ output.add(" ");
+ }
+ this.operands[1].genCSS(context, output);
+};
+
+module.exports = Operation;
--- /dev/null
+var Node = require("./node");
+
+var Paren = function (node) {
+ this.value = node;
+};
+Paren.prototype = new Node();
+Paren.prototype.type = "Paren";
+Paren.prototype.genCSS = function (context, output) {
+ output.add('(');
+ this.value.genCSS(context, output);
+ output.add(')');
+};
+Paren.prototype.eval = function (context) {
+ return new Paren(this.value.eval(context));
+};
+module.exports = Paren;
--- /dev/null
+var Node = require("./node"),
+ JsEvalNode = require("./js-eval-node"),
+ Variable = require("./variable");
+
+var Quoted = function (str, content, escaped, index, currentFileInfo) {
+ this.escaped = (escaped == null) ? true : escaped;
+ this.value = content || '';
+ this.quote = str.charAt(0);
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+};
+Quoted.prototype = new JsEvalNode();
+Quoted.prototype.type = "Quoted";
+Quoted.prototype.genCSS = function (context, output) {
+ if (!this.escaped) {
+ output.add(this.quote, this.currentFileInfo, this.index);
+ }
+ output.add(this.value);
+ if (!this.escaped) {
+ output.add(this.quote);
+ }
+};
+Quoted.prototype.containsVariables = function() {
+ return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/);
+};
+Quoted.prototype.eval = function (context) {
+ var that = this, value = this.value;
+ var javascriptReplacement = function (_, exp) {
+ return String(that.evaluateJavaScript(exp, context));
+ };
+ var interpolationReplacement = function (_, name) {
+ var v = new Variable('@' + name, that.index, that.currentFileInfo).eval(context, true);
+ return (v instanceof Quoted) ? v.value : v.toCSS();
+ };
+ function iterativeReplace(value, regexp, replacementFnc) {
+ var evaluatedValue = value;
+ do {
+ value = evaluatedValue;
+ evaluatedValue = value.replace(regexp, replacementFnc);
+ } while (value !== evaluatedValue);
+ return evaluatedValue;
+ }
+ value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement);
+ value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement);
+ return new Quoted(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
+};
+Quoted.prototype.compare = function (other) {
+ // when comparing quoted strings allow the quote to differ
+ if (other.type === "Quoted" && !this.escaped && !other.escaped) {
+ return Node.numericCompare(this.value, other.value);
+ } else {
+ return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
+ }
+};
+module.exports = Quoted;
--- /dev/null
+var Node = require("./node"),
+ Value = require("./value"),
+ Keyword = require("./keyword");
+
+var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
+ this.name = name;
+ this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
+ this.important = important ? ' ' + important.trim() : '';
+ this.merge = merge;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo;
+ this.inline = inline || false;
+ this.variable = (variable !== undefined) ? variable
+ : (name.charAt && (name.charAt(0) === '@'));
+};
+
+function evalName(context, name) {
+ var value = "", i, n = name.length,
+ output = {add: function (s) {value += s;}};
+ for (i = 0; i < n; i++) {
+ name[i].eval(context).genCSS(context, output);
+ }
+ return value;
+}
+
+Rule.prototype = new Node();
+Rule.prototype.type = "Rule";
+Rule.prototype.genCSS = function (context, output) {
+ output.add(this.name + (context.compress ? ':' : ': '), this.currentFileInfo, this.index);
+ try {
+ this.value.genCSS(context, output);
+ }
+ catch(e) {
+ e.index = this.index;
+ e.filename = this.currentFileInfo.filename;
+ throw e;
+ }
+ output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this.currentFileInfo, this.index);
+};
+Rule.prototype.eval = function (context) {
+ var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable;
+ if (typeof name !== "string") {
+ // expand 'primitive' name directly to get
+ // things faster (~10% for benchmark.less):
+ name = (name.length === 1) && (name[0] instanceof Keyword) ?
+ name[0].value : evalName(context, name);
+ variable = false; // never treat expanded interpolation as new variable name
+ }
+ if (name === "font" && !context.strictMath) {
+ strictMathBypass = true;
+ context.strictMath = true;
+ }
+ try {
+ context.importantScope.push({});
+ evaldValue = this.value.eval(context);
+
+ if (!this.variable && evaldValue.type === "DetachedRuleset") {
+ throw { message: "Rulesets cannot be evaluated on a property.",
+ index: this.index, filename: this.currentFileInfo.filename };
+ }
+ var important = this.important,
+ importantResult = context.importantScope.pop();
+ if (!important && importantResult.important) {
+ important = importantResult.important;
+ }
+
+ return new Rule(name,
+ evaldValue,
+ important,
+ this.merge,
+ this.index, this.currentFileInfo, this.inline,
+ variable);
+ }
+ catch(e) {
+ if (typeof e.index !== 'number') {
+ e.index = this.index;
+ e.filename = this.currentFileInfo.filename;
+ }
+ throw e;
+ }
+ finally {
+ if (strictMathBypass) {
+ context.strictMath = false;
+ }
+ }
+};
+Rule.prototype.makeImportant = function () {
+ return new Rule(this.name,
+ this.value,
+ "!important",
+ this.merge,
+ this.index, this.currentFileInfo, this.inline);
+};
+
+module.exports = Rule;
--- /dev/null
+var Node = require("./node"),
+ Variable = require("./variable");
+
+var RulesetCall = function (variable) {
+ this.variable = variable;
+};
+RulesetCall.prototype = new Node();
+RulesetCall.prototype.type = "RulesetCall";
+RulesetCall.prototype.eval = function (context) {
+ var detachedRuleset = new Variable(this.variable).eval(context);
+ return detachedRuleset.callEval(context);
+};
+module.exports = RulesetCall;
--- /dev/null
+var Node = require("./node"),
+ Rule = require("./rule"),
+ Selector = require("./selector"),
+ Element = require("./element"),
+ Paren = require("./paren"),
+ contexts = require("../contexts"),
+ defaultFunc = require("../functions/default"),
+ getDebugInfo = require("./debug-info");
+
+var Ruleset = function (selectors, rules, strictImports) {
+ this.selectors = selectors;
+ this.rules = rules;
+ this._lookups = {};
+ this.strictImports = strictImports;
+};
+Ruleset.prototype = new Node();
+Ruleset.prototype.type = "Ruleset";
+Ruleset.prototype.isRuleset = true;
+Ruleset.prototype.isRulesetLike = true;
+Ruleset.prototype.accept = function (visitor) {
+ if (this.paths) {
+ visitor.visitArray(this.paths, true);
+ } else if (this.selectors) {
+ this.selectors = visitor.visitArray(this.selectors);
+ }
+ if (this.rules && this.rules.length) {
+ this.rules = visitor.visitArray(this.rules);
+ }
+};
+Ruleset.prototype.eval = function (context) {
+ var thisSelectors = this.selectors, selectors,
+ selCnt, selector, i, hasOnePassingSelector = false;
+
+ if (thisSelectors && (selCnt = thisSelectors.length)) {
+ selectors = [];
+ defaultFunc.error({
+ type: "Syntax",
+ message: "it is currently only allowed in parametric mixin guards,"
+ });
+ for (i = 0; i < selCnt; i++) {
+ selector = thisSelectors[i].eval(context);
+ selectors.push(selector);
+ if (selector.evaldCondition) {
+ hasOnePassingSelector = true;
+ }
+ }
+ defaultFunc.reset();
+ } else {
+ hasOnePassingSelector = true;
+ }
+
+ var rules = this.rules ? this.rules.slice(0) : null,
+ ruleset = new Ruleset(selectors, rules, this.strictImports),
+ rule, subRule;
+
+ ruleset.originalRuleset = this;
+ ruleset.root = this.root;
+ ruleset.firstRoot = this.firstRoot;
+ ruleset.allowImports = this.allowImports;
+
+ if (this.debugInfo) {
+ ruleset.debugInfo = this.debugInfo;
+ }
+
+ if (!hasOnePassingSelector) {
+ rules.length = 0;
+ }
+
+ // push the current ruleset to the frames stack
+ var ctxFrames = context.frames;
+ ctxFrames.unshift(ruleset);
+
+ // currrent selectors
+ var ctxSelectors = context.selectors;
+ if (!ctxSelectors) {
+ context.selectors = ctxSelectors = [];
+ }
+ ctxSelectors.unshift(this.selectors);
+
+ // Evaluate imports
+ if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
+ ruleset.evalImports(context);
+ }
+
+ // Store the frames around mixin definitions,
+ // so they can be evaluated like closures when the time comes.
+ var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
+ for (i = 0; i < rsRuleCnt; i++) {
+ if (rsRules[i].evalFirst) {
+ rsRules[i] = rsRules[i].eval(context);
+ }
+ }
+
+ var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
+
+ // Evaluate mixin calls.
+ for (i = 0; i < rsRuleCnt; i++) {
+ if (rsRules[i].type === "MixinCall") {
+ /*jshint loopfunc:true */
+ rules = rsRules[i].eval(context).filter(function(r) {
+ if ((r instanceof Rule) && r.variable) {
+ // do not pollute the scope if the variable is
+ // already there. consider returning false here
+ // but we need a way to "return" variable from mixins
+ return !(ruleset.variable(r.name));
+ }
+ return true;
+ });
+ rsRules.splice.apply(rsRules, [i, 1].concat(rules));
+ rsRuleCnt += rules.length - 1;
+ i += rules.length - 1;
+ ruleset.resetCache();
+ } else if (rsRules[i].type === "RulesetCall") {
+ /*jshint loopfunc:true */
+ rules = rsRules[i].eval(context).rules.filter(function(r) {
+ if ((r instanceof Rule) && r.variable) {
+ // do not pollute the scope at all
+ return false;
+ }
+ return true;
+ });
+ rsRules.splice.apply(rsRules, [i, 1].concat(rules));
+ rsRuleCnt += rules.length - 1;
+ i += rules.length - 1;
+ ruleset.resetCache();
+ }
+ }
+
+ // Evaluate everything else
+ for (i = 0; i < rsRules.length; i++) {
+ rule = rsRules[i];
+ if (!rule.evalFirst) {
+ rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
+ }
+ }
+
+ // Evaluate everything else
+ for (i = 0; i < rsRules.length; i++) {
+ rule = rsRules[i];
+ // for rulesets, check if it is a css guard and can be removed
+ if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
+ // check if it can be folded in (e.g. & where)
+ if (rule.selectors[0].isJustParentSelector()) {
+ rsRules.splice(i--, 1);
+
+ for (var j = 0; j < rule.rules.length; j++) {
+ subRule = rule.rules[j];
+ if (!(subRule instanceof Rule) || !subRule.variable) {
+ rsRules.splice(++i, 0, subRule);
+ }
+ }
+ }
+ }
+ }
+
+ // Pop the stack
+ ctxFrames.shift();
+ ctxSelectors.shift();
+
+ if (context.mediaBlocks) {
+ for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) {
+ context.mediaBlocks[i].bubbleSelectors(selectors);
+ }
+ }
+
+ return ruleset;
+};
+Ruleset.prototype.evalImports = function(context) {
+ var rules = this.rules, i, importRules;
+ if (!rules) { return; }
+
+ for (i = 0; i < rules.length; i++) {
+ if (rules[i].type === "Import") {
+ importRules = rules[i].eval(context);
+ if (importRules && importRules.length) {
+ rules.splice.apply(rules, [i, 1].concat(importRules));
+ i+= importRules.length - 1;
+ } else {
+ rules.splice(i, 1, importRules);
+ }
+ this.resetCache();
+ }
+ }
+};
+Ruleset.prototype.makeImportant = function() {
+ var result = new Ruleset(this.selectors, this.rules.map(function (r) {
+ if (r.makeImportant) {
+ return r.makeImportant();
+ } else {
+ return r;
+ }
+ }), this.strictImports);
+
+ return result;
+};
+Ruleset.prototype.matchArgs = function (args) {
+ return !args || args.length === 0;
+};
+// lets you call a css selector with a guard
+Ruleset.prototype.matchCondition = function (args, context) {
+ var lastSelector = this.selectors[this.selectors.length - 1];
+ if (!lastSelector.evaldCondition) {
+ return false;
+ }
+ if (lastSelector.condition &&
+ !lastSelector.condition.eval(
+ new contexts.Eval(context,
+ context.frames))) {
+ return false;
+ }
+ return true;
+};
+Ruleset.prototype.resetCache = function () {
+ this._rulesets = null;
+ this._variables = null;
+ this._lookups = {};
+};
+Ruleset.prototype.variables = function () {
+ if (!this._variables) {
+ this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
+ if (r instanceof Rule && r.variable === true) {
+ hash[r.name] = r;
+ }
+ // when evaluating variables in an import statement, imports have not been eval'd
+ // so we need to go inside import statements.
+ // guard against root being a string (in the case of inlined less)
+ if (r.type === "Import" && r.root && r.root.variables) {
+ var vars = r.root.variables();
+ for (var name in vars) {
+ if (vars.hasOwnProperty(name)) {
+ hash[name] = vars[name];
+ }
+ }
+ }
+ return hash;
+ }, {});
+ }
+ return this._variables;
+};
+Ruleset.prototype.variable = function (name) {
+ return this.variables()[name];
+};
+Ruleset.prototype.rulesets = function () {
+ if (!this.rules) { return null; }
+
+ var filtRules = [], rules = this.rules, cnt = rules.length,
+ i, rule;
+
+ for (i = 0; i < cnt; i++) {
+ rule = rules[i];
+ if (rule.isRuleset) {
+ filtRules.push(rule);
+ }
+ }
+
+ return filtRules;
+};
+Ruleset.prototype.prependRule = function (rule) {
+ var rules = this.rules;
+ if (rules) {
+ rules.unshift(rule);
+ } else {
+ this.rules = [ rule ];
+ }
+};
+Ruleset.prototype.find = function (selector, self, filter) {
+ self = self || this;
+ var rules = [], match, foundMixins,
+ key = selector.toCSS();
+
+ if (key in this._lookups) { return this._lookups[key]; }
+
+ this.rulesets().forEach(function (rule) {
+ if (rule !== self) {
+ for (var j = 0; j < rule.selectors.length; j++) {
+ match = selector.match(rule.selectors[j]);
+ if (match) {
+ if (selector.elements.length > match) {
+ if (!filter || filter(rule)) {
+ foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter);
+ for (var i = 0; i < foundMixins.length; ++i) {
+ foundMixins[i].path.push(rule);
+ }
+ Array.prototype.push.apply(rules, foundMixins);
+ }
+ } else {
+ rules.push({ rule: rule, path: []});
+ }
+ break;
+ }
+ }
+ }
+ });
+ this._lookups[key] = rules;
+ return rules;
+};
+Ruleset.prototype.genCSS = function (context, output) {
+ var i, j,
+ charsetRuleNodes = [],
+ ruleNodes = [],
+ rulesetNodes = [],
+ rulesetNodeCnt,
+ debugInfo, // Line number debugging
+ rule,
+ path;
+
+ context.tabLevel = (context.tabLevel || 0);
+
+ if (!this.root) {
+ context.tabLevel++;
+ }
+
+ var tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(" "),
+ tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "),
+ sep;
+
+ function isRulesetLikeNode(rule, root) {
+ // if it has nested rules, then it should be treated like a ruleset
+ // medias and comments do not have nested rules, but should be treated like rulesets anyway
+ // some directives and anonymous nodes are ruleset like, others are not
+ if (typeof rule.isRulesetLike === "boolean") {
+ return rule.isRulesetLike;
+ } else if (typeof rule.isRulesetLike === "function") {
+ return rule.isRulesetLike(root);
+ }
+
+ //anything else is assumed to be a rule
+ return false;
+ }
+
+ for (i = 0; i < this.rules.length; i++) {
+ rule = this.rules[i];
+ if (isRulesetLikeNode(rule, this.root)) {
+ rulesetNodes.push(rule);
+ } else {
+ //charsets should float on top of everything
+ if (rule.isCharset && rule.isCharset()) {
+ charsetRuleNodes.push(rule);
+ } else {
+ ruleNodes.push(rule);
+ }
+ }
+ }
+ ruleNodes = charsetRuleNodes.concat(ruleNodes);
+
+ // If this is the root node, we don't render
+ // a selector, or {}.
+ if (!this.root) {
+ debugInfo = getDebugInfo(context, this, tabSetStr);
+
+ if (debugInfo) {
+ output.add(debugInfo);
+ output.add(tabSetStr);
+ }
+
+ var paths = this.paths, pathCnt = paths.length,
+ pathSubCnt;
+
+ sep = context.compress ? ',' : (',\n' + tabSetStr);
+
+ for (i = 0; i < pathCnt; i++) {
+ path = paths[i];
+ if (!(pathSubCnt = path.length)) { continue; }
+ if (i > 0) { output.add(sep); }
+
+ context.firstSelector = true;
+ path[0].genCSS(context, output);
+
+ context.firstSelector = false;
+ for (j = 1; j < pathSubCnt; j++) {
+ path[j].genCSS(context, output);
+ }
+ }
+
+ output.add((context.compress ? '{' : ' {\n') + tabRuleStr);
+ }
+
+ // Compile rules and rulesets
+ for (i = 0; i < ruleNodes.length; i++) {
+ rule = ruleNodes[i];
+
+ // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
+ // In this instance we do not know whether it is the last property
+ if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) {
+ context.lastRule = true;
+ }
+
+ if (rule.genCSS) {
+ rule.genCSS(context, output);
+ } else if (rule.value) {
+ output.add(rule.value.toString());
+ }
+
+ if (!context.lastRule) {
+ output.add(context.compress ? '' : ('\n' + tabRuleStr));
+ } else {
+ context.lastRule = false;
+ }
+ }
+
+ if (!this.root) {
+ output.add((context.compress ? '}' : '\n' + tabSetStr + '}'));
+ context.tabLevel--;
+ }
+
+ sep = (context.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr);
+ rulesetNodeCnt = rulesetNodes.length;
+ if (rulesetNodeCnt) {
+ if (ruleNodes.length && sep) { output.add(sep); }
+ rulesetNodes[0].genCSS(context, output);
+ for (i = 1; i < rulesetNodeCnt; i++) {
+ if (sep) { output.add(sep); }
+ rulesetNodes[i].genCSS(context, output);
+ }
+ }
+
+ if (!output.isEmpty() && !context.compress && this.firstRoot) {
+ output.add('\n');
+ }
+};
+Ruleset.prototype.markReferenced = function () {
+ var s;
+ if (this.selectors) {
+ for (s = 0; s < this.selectors.length; s++) {
+ this.selectors[s].markReferenced();
+ }
+ }
+
+ if (this.rules) {
+ for (s = 0; s < this.rules.length; s++) {
+ if (this.rules[s].markReferenced) {
+ this.rules[s].markReferenced();
+ }
+ }
+ }
+};
+Ruleset.prototype.getIsReferenced = function() {
+ var i, j, path, selector;
+
+ if (this.paths) {
+ for (i = 0; i < this.paths.length; i++) {
+ path = this.paths[i];
+ for (j = 0; j < path.length; j++) {
+ if (path[j].getIsReferenced && path[j].getIsReferenced()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ if (this.selectors) {
+ for (i = 0; i < this.selectors.length; i++) {
+ selector = this.selectors[i];
+ if (selector.getIsReferenced && selector.getIsReferenced()) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
+ for (var s = 0; s < selectors.length; s++) {
+ this.joinSelector(paths, context, selectors[s]);
+ }
+};
+
+Ruleset.prototype.joinSelector = function (paths, context, selector) {
+
+ function createParenthesis(elementsToPak, originalElement) {
+ var replacementParen, j;
+ if (elementsToPak.length === 0) {
+ replacementParen = new Paren(elementsToPak[0]);
+ } else {
+ var insideParent = [];
+ for (j = 0; j < elementsToPak.length; j++) {
+ insideParent.push(new Element(null, elementsToPak[j], originalElement.index, originalElement.currentFileInfo));
+ }
+ replacementParen = new Paren(new Selector(insideParent));
+ }
+ return replacementParen;
+ }
+
+ function createSelector(containedElement, originalElement) {
+ var element, selector;
+ element = new Element(null, containedElement, originalElement.index, originalElement.currentFileInfo);
+ selector = new Selector([element]);
+ return selector;
+ }
+
+ // replace all parent selectors inside `inSelector` by content of `context` array
+ // resulting selectors are returned inside `paths` array
+ // returns true if `inSelector` contained at least one parent selector
+ function replaceParentSelector(paths, context, inSelector) {
+ // The paths are [[Selector]]
+ // The first list is a list of comma separated selectors
+ // The inner list is a list of inheritance separated selectors
+ // e.g.
+ // .a, .b {
+ // .c {
+ // }
+ // }
+ // == [[.a] [.c]] [[.b] [.c]]
+ //
+ var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector;
+ function findNestedSelector(element) {
+ var maybeSelector;
+ if (element.value.type !== 'Paren') {
+ return null;
+ }
+
+ maybeSelector = element.value.value;
+ if (maybeSelector.type !== 'Selector') {
+ return null;
+ }
+
+ return maybeSelector;
+ }
+
+ // the elements from the current selector so far
+ currentElements = [];
+ // the current list of new selectors to add to the path.
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
+ // by the parents
+ newSelectors = [
+ []
+ ];
+
+ for (i = 0; i < inSelector.elements.length; i++) {
+ el = inSelector.elements[i];
+ // non parent reference elements just get added
+ if (el.value !== "&") {
+ var nestedSelector = findNestedSelector(el);
+ if (nestedSelector != null) {
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ var nestedPaths = [], replaced, replacedNewSelectors = [];
+ replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
+ hadParentSelector = hadParentSelector || replaced;
+ //the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
+ for (k = 0; k < nestedPaths.length; k++) {
+ var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
+ addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
+ }
+ newSelectors = replacedNewSelectors;
+ currentElements = [];
+
+ } else {
+ currentElements.push(el);
+ }
+
+ } else {
+ hadParentSelector = true;
+ // the new list of selectors to add
+ selectorsMultiplied = [];
+
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ // loop through our current selectors
+ for (j = 0; j < newSelectors.length; j++) {
+ sel = newSelectors[j];
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
+ // whether there are parents or not
+ if (context.length === 0) {
+ // the combinator used on el should now be applied to the next element instead so that
+ // it is not lost
+ if (sel.length > 0) {
+ sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo));
+ }
+ selectorsMultiplied.push(sel);
+ }
+ else {
+ // and the parent selectors
+ for (k = 0; k < context.length; k++) {
+ // We need to put the current selectors
+ // then join the last selector's elements on to the parents selectors
+ var newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
+ // add that to our new set of selectors
+ selectorsMultiplied.push(newSelectorPath);
+ }
+ }
+ }
+
+ // our new selectors has been multiplied, so reset the state
+ newSelectors = selectorsMultiplied;
+ currentElements = [];
+ }
+ }
+
+ // if we have any elements left over (e.g. .a& .b == .b)
+ // add them on to all the current selectors
+ mergeElementsOnToSelectors(currentElements, newSelectors);
+
+ for (i = 0; i < newSelectors.length; i++) {
+ length = newSelectors[i].length;
+ if (length > 0) {
+ paths.push(newSelectors[i]);
+ lastSelector = newSelectors[i][length - 1];
+ newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
+ }
+ }
+
+ return hadParentSelector;
+ }
+
+ // joins selector path from `beginningPath` with selector path in `addPath`
+ // `replacedElement` contains element that is being replaced by `addPath`
+ // returns concatenated path
+ function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
+ var newSelectorPath, lastSelector, newJoinedSelector;
+ // our new selector path
+ newSelectorPath = [];
+
+ //construct the joined selector - if & is the first thing this will be empty,
+ // if not newJoinedSelector will be the last set of elements in the selector
+ if (beginningPath.length > 0) {
+ newSelectorPath = beginningPath.slice(0);
+ lastSelector = newSelectorPath.pop();
+ newJoinedSelector = originalSelector.createDerived(lastSelector.elements.slice(0));
+ }
+ else {
+ newJoinedSelector = originalSelector.createDerived([]);
+ }
+
+ if (addPath.length > 0) {
+ // /deep/ is a combinator that is valid without anything in front of it
+ // so if the & does not have a combinator that is "" or " " then
+ // and there is a combinator on the parent, then grab that.
+ // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
+ var combinator = replacedElement.combinator, parentEl = addPath[0].elements[0];
+ if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
+ combinator = parentEl.combinator;
+ }
+ // join the elements so far with the first part of the parent
+ newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement.index, replacedElement.currentFileInfo));
+ newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
+ }
+
+ // now add the joined selector - but only if it is not empty
+ if (newJoinedSelector.elements.length !== 0) {
+ newSelectorPath.push(newJoinedSelector);
+ }
+
+ //put together the parent selectors after the join (e.g. the rest of the parent)
+ if (addPath.length > 1) {
+ newSelectorPath = newSelectorPath.concat(addPath.slice(1));
+ }
+ return newSelectorPath;
+ }
+
+ // joins selector path from `beginningPath` with every selector path in `addPaths` array
+ // `replacedElement` contains element that is being replaced by `addPath`
+ // returns array with all concatenated paths
+ function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector, result) {
+ var j;
+ for (j = 0; j < beginningPath.length; j++) {
+ var newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
+ result.push(newSelectorPath);
+ }
+ return result;
+ }
+
+ function mergeElementsOnToSelectors(elements, selectors) {
+ var i, sel;
+
+ if (elements.length === 0) {
+ return ;
+ }
+ if (selectors.length === 0) {
+ selectors.push([ new Selector(elements) ]);
+ return;
+ }
+
+ for (i = 0; i < selectors.length; i++) {
+ sel = selectors[i];
+
+ // if the previous thing in sel is a parent this needs to join on to it
+ if (sel.length > 0) {
+ sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
+ }
+ else {
+ sel.push(new Selector(elements));
+ }
+ }
+ }
+
+ // joinSelector code follows
+ var i, newPaths, hadParentSelector;
+
+ newPaths = [];
+ hadParentSelector = replaceParentSelector(newPaths, context, selector);
+
+ if (!hadParentSelector) {
+ if (context.length > 0) {
+ newPaths = [];
+ for (i = 0; i < context.length; i++) {
+ newPaths.push(context[i].concat(selector));
+ }
+ }
+ else {
+ newPaths = [[selector]];
+ }
+ }
+
+ for (i = 0; i < newPaths.length; i++) {
+ paths.push(newPaths[i]);
+ }
+
+};
+module.exports = Ruleset;
--- /dev/null
+var Node = require("./node");
+
+var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) {
+ this.elements = elements;
+ this.extendList = extendList;
+ this.condition = condition;
+ this.currentFileInfo = currentFileInfo || {};
+ this.isReferenced = isReferenced;
+ if (!condition) {
+ this.evaldCondition = true;
+ }
+};
+Selector.prototype = new Node();
+Selector.prototype.type = "Selector";
+Selector.prototype.accept = function (visitor) {
+ if (this.elements) {
+ this.elements = visitor.visitArray(this.elements);
+ }
+ if (this.extendList) {
+ this.extendList = visitor.visitArray(this.extendList);
+ }
+ if (this.condition) {
+ this.condition = visitor.visit(this.condition);
+ }
+};
+Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
+ evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
+ var newSelector = new Selector(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
+ newSelector.evaldCondition = evaldCondition;
+ newSelector.mediaEmpty = this.mediaEmpty;
+ return newSelector;
+};
+Selector.prototype.match = function (other) {
+ var elements = this.elements,
+ len = elements.length,
+ olen, i;
+
+ other.CacheElements();
+
+ olen = other._elements.length;
+ if (olen === 0 || len < olen) {
+ return 0;
+ } else {
+ for (i = 0; i < olen; i++) {
+ if (elements[i].value !== other._elements[i]) {
+ return 0;
+ }
+ }
+ }
+
+ return olen; // return number of matched elements
+};
+Selector.prototype.CacheElements = function() {
+ if (this._elements) {
+ return;
+ }
+
+ var elements = this.elements.map( function(v) {
+ return v.combinator.value + (v.value.value || v.value);
+ }).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
+
+ if (elements) {
+ if (elements[0] === "&") {
+ elements.shift();
+ }
+ } else {
+ elements = [];
+ }
+
+ this._elements = elements;
+};
+Selector.prototype.isJustParentSelector = function() {
+ return !this.mediaEmpty &&
+ this.elements.length === 1 &&
+ this.elements[0].value === '&' &&
+ (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
+};
+Selector.prototype.eval = function (context) {
+ var evaldCondition = this.condition && this.condition.eval(context),
+ elements = this.elements, extendList = this.extendList;
+
+ elements = elements && elements.map(function (e) { return e.eval(context); });
+ extendList = extendList && extendList.map(function(extend) { return extend.eval(context); });
+
+ return this.createDerived(elements, extendList, evaldCondition);
+};
+Selector.prototype.genCSS = function (context, output) {
+ var i, element;
+ if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") {
+ output.add(' ', this.currentFileInfo, this.index);
+ }
+ if (!this._css) {
+ //TODO caching? speed comparison?
+ for (i = 0; i < this.elements.length; i++) {
+ element = this.elements[i];
+ element.genCSS(context, output);
+ }
+ }
+};
+Selector.prototype.markReferenced = function () {
+ this.isReferenced = true;
+};
+Selector.prototype.getIsReferenced = function() {
+ return !this.currentFileInfo.reference || this.isReferenced;
+};
+Selector.prototype.getIsOutput = function() {
+ return this.evaldCondition;
+};
+module.exports = Selector;
--- /dev/null
+var Node = require("./node");
+
+var UnicodeDescriptor = function (value) {
+ this.value = value;
+};
+UnicodeDescriptor.prototype = new Node();
+UnicodeDescriptor.prototype.type = "UnicodeDescriptor";
+
+module.exports = UnicodeDescriptor;
--- /dev/null
+var Node = require("./node"),
+ unitConversions = require("../data/unit-conversions");
+
+var Unit = function (numerator, denominator, backupUnit) {
+ this.numerator = numerator ? numerator.slice(0).sort() : [];
+ this.denominator = denominator ? denominator.slice(0).sort() : [];
+ if (backupUnit) {
+ this.backupUnit = backupUnit;
+ } else if (numerator && numerator.length) {
+ this.backupUnit = numerator[0];
+ }
+};
+
+Unit.prototype = new Node();
+Unit.prototype.type = "Unit";
+Unit.prototype.clone = function () {
+ return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
+};
+Unit.prototype.genCSS = function (context, output) {
+ // Dimension checks the unit is singular and throws an error if in strict math mode.
+ var strictUnits = context && context.strictUnits;
+ if (this.numerator.length === 1) {
+ output.add(this.numerator[0]); // the ideal situation
+ } else if (!strictUnits && this.backupUnit) {
+ output.add(this.backupUnit);
+ } else if (!strictUnits && this.denominator.length) {
+ output.add(this.denominator[0]);
+ }
+};
+Unit.prototype.toString = function () {
+ var i, returnStr = this.numerator.join("*");
+ for (i = 0; i < this.denominator.length; i++) {
+ returnStr += "/" + this.denominator[i];
+ }
+ return returnStr;
+};
+Unit.prototype.compare = function (other) {
+ return this.is(other.toString()) ? 0 : undefined;
+};
+Unit.prototype.is = function (unitString) {
+ return this.toString().toUpperCase() === unitString.toUpperCase();
+};
+Unit.prototype.isLength = function () {
+ return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
+};
+Unit.prototype.isEmpty = function () {
+ return this.numerator.length === 0 && this.denominator.length === 0;
+};
+Unit.prototype.isSingular = function() {
+ return this.numerator.length <= 1 && this.denominator.length === 0;
+};
+Unit.prototype.map = function(callback) {
+ var i;
+
+ for (i = 0; i < this.numerator.length; i++) {
+ this.numerator[i] = callback(this.numerator[i], false);
+ }
+
+ for (i = 0; i < this.denominator.length; i++) {
+ this.denominator[i] = callback(this.denominator[i], true);
+ }
+};
+Unit.prototype.usedUnits = function() {
+ var group, result = {}, mapUnit;
+
+ mapUnit = function (atomicUnit) {
+ /*jshint loopfunc:true */
+ if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
+ result[groupName] = atomicUnit;
+ }
+
+ return atomicUnit;
+ };
+
+ for (var groupName in unitConversions) {
+ if (unitConversions.hasOwnProperty(groupName)) {
+ group = unitConversions[groupName];
+
+ this.map(mapUnit);
+ }
+ }
+
+ return result;
+};
+Unit.prototype.cancel = function () {
+ var counter = {}, atomicUnit, i;
+
+ for (i = 0; i < this.numerator.length; i++) {
+ atomicUnit = this.numerator[i];
+ counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
+ }
+
+ for (i = 0; i < this.denominator.length; i++) {
+ atomicUnit = this.denominator[i];
+ counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
+ }
+
+ this.numerator = [];
+ this.denominator = [];
+
+ for (atomicUnit in counter) {
+ if (counter.hasOwnProperty(atomicUnit)) {
+ var count = counter[atomicUnit];
+
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ this.numerator.push(atomicUnit);
+ }
+ } else if (count < 0) {
+ for (i = 0; i < -count; i++) {
+ this.denominator.push(atomicUnit);
+ }
+ }
+ }
+ }
+
+ this.numerator.sort();
+ this.denominator.sort();
+};
+module.exports = Unit;
--- /dev/null
+var Node = require("./node");
+
+var URL = function (val, index, currentFileInfo, isEvald) {
+ this.value = val;
+ this.currentFileInfo = currentFileInfo;
+ this.index = index;
+ this.isEvald = isEvald;
+};
+URL.prototype = new Node();
+URL.prototype.type = "Url";
+URL.prototype.accept = function (visitor) {
+ this.value = visitor.visit(this.value);
+};
+URL.prototype.genCSS = function (context, output) {
+ output.add("url(");
+ this.value.genCSS(context, output);
+ output.add(")");
+};
+URL.prototype.eval = function (context) {
+ var val = this.value.eval(context),
+ rootpath;
+
+ if (!this.isEvald) {
+ // Add the base path if the URL is relative
+ rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
+ if (rootpath &&
+ typeof val.value === "string" &&
+ context.isPathRelative(val.value)) {
+
+ if (!val.quote) {
+ rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\" + match; });
+ }
+ val.value = rootpath + val.value;
+ }
+
+ val.value = context.normalizePath(val.value);
+
+ // Add url args if enabled
+ if (context.urlArgs) {
+ if (!val.value.match(/^\s*data:/)) {
+ var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
+ var urlArgs = delimiter + context.urlArgs;
+ if (val.value.indexOf('#') !== -1) {
+ val.value = val.value.replace('#', urlArgs + '#');
+ } else {
+ val.value += urlArgs;
+ }
+ }
+ }
+ }
+
+ return new URL(val, this.index, this.currentFileInfo, true);
+};
+module.exports = URL;
--- /dev/null
+var Node = require("./node");
+
+var Value = function (value) {
+ this.value = value;
+ if (!value) {
+ throw new Error("Value requires an array argument");
+ }
+};
+Value.prototype = new Node();
+Value.prototype.type = "Value";
+Value.prototype.accept = function (visitor) {
+ if (this.value) {
+ this.value = visitor.visitArray(this.value);
+ }
+};
+Value.prototype.eval = function (context) {
+ if (this.value.length === 1) {
+ return this.value[0].eval(context);
+ } else {
+ return new Value(this.value.map(function (v) {
+ return v.eval(context);
+ }));
+ }
+};
+Value.prototype.genCSS = function (context, output) {
+ var i;
+ for (i = 0; i < this.value.length; i++) {
+ this.value[i].genCSS(context, output);
+ if (i + 1 < this.value.length) {
+ output.add((context && context.compress) ? ',' : ', ');
+ }
+ }
+};
+module.exports = Value;
--- /dev/null
+var Node = require("./node");
+
+var Variable = function (name, index, currentFileInfo) {
+ this.name = name;
+ this.index = index;
+ this.currentFileInfo = currentFileInfo || {};
+};
+Variable.prototype = new Node();
+Variable.prototype.type = "Variable";
+Variable.prototype.eval = function (context) {
+ var variable, name = this.name;
+
+ if (name.indexOf('@@') === 0) {
+ name = '@' + new Variable(name.slice(1), this.index, this.currentFileInfo).eval(context).value;
+ }
+
+ if (this.evaluating) {
+ throw { type: 'Name',
+ message: "Recursive variable definition for " + name,
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+
+ this.evaluating = true;
+
+ variable = this.find(context.frames, function (frame) {
+ var v = frame.variable(name);
+ if (v) {
+ if (v.important) {
+ var importantScope = context.importantScope[context.importantScope.length - 1];
+ importantScope.important = v.important;
+ }
+ return v.value.eval(context);
+ }
+ });
+ if (variable) {
+ this.evaluating = false;
+ return variable;
+ } else {
+ throw { type: 'Name',
+ message: "variable " + name + " is undefined",
+ filename: this.currentFileInfo.filename,
+ index: this.index };
+ }
+};
+Variable.prototype.find = function (obj, fun) {
+ for (var i = 0, r; i < obj.length; i++) {
+ r = fun.call(obj, obj[i]);
+ if (r) { return r; }
+ }
+ return null;
+};
+module.exports = Variable;
--- /dev/null
+module.exports = {
+ getLocation: function(index, inputStream) {
+ var n = index + 1,
+ line = null,
+ column = -1;
+
+ while (--n >= 0 && inputStream.charAt(n) !== '\n') {
+ column++;
+ }
+
+ if (typeof index === 'number') {
+ line = (inputStream.slice(0, index).match(/\n/g) || "").length;
+ }
+
+ return {
+ line: line,
+ column: column
+ };
+ }
+};
--- /dev/null
+var tree = require("../tree"),
+ Visitor = require("./visitor"),
+ logger = require("../logger");
+
+/*jshint loopfunc:true */
+
+var ExtendFinderVisitor = function() {
+ this._visitor = new Visitor(this);
+ this.contexts = [];
+ this.allExtendsStack = [[]];
+};
+
+ExtendFinderVisitor.prototype = {
+ run: function (root) {
+ root = this._visitor.visit(root);
+ root.allExtends = this.allExtendsStack[0];
+ return root;
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitRuleset: function (rulesetNode, visitArgs) {
+ if (rulesetNode.root) {
+ return;
+ }
+
+ var i, j, extend, allSelectorsExtendList = [], extendList;
+
+ // get &:extend(.a); rules which apply to all selectors in this ruleset
+ var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0;
+ for (i = 0; i < ruleCnt; i++) {
+ if (rulesetNode.rules[i] instanceof tree.Extend) {
+ allSelectorsExtendList.push(rules[i]);
+ rulesetNode.extendOnEveryPath = true;
+ }
+ }
+
+ // now find every selector and apply the extends that apply to all extends
+ // and the ones which apply to an individual extend
+ var paths = rulesetNode.paths;
+ for (i = 0; i < paths.length; i++) {
+ var selectorPath = paths[i],
+ selector = selectorPath[selectorPath.length - 1],
+ selExtendList = selector.extendList;
+
+ extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList)
+ : allSelectorsExtendList;
+
+ if (extendList) {
+ extendList = extendList.map(function(allSelectorsExtend) {
+ return allSelectorsExtend.clone();
+ });
+ }
+
+ for (j = 0; j < extendList.length; j++) {
+ this.foundExtends = true;
+ extend = extendList[j];
+ extend.findSelfSelectors(selectorPath);
+ extend.ruleset = rulesetNode;
+ if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
+ this.allExtendsStack[this.allExtendsStack.length - 1].push(extend);
+ }
+ }
+
+ this.contexts.push(rulesetNode.selectors);
+ },
+ visitRulesetOut: function (rulesetNode) {
+ if (!rulesetNode.root) {
+ this.contexts.length = this.contexts.length - 1;
+ }
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ mediaNode.allExtends = [];
+ this.allExtendsStack.push(mediaNode.allExtends);
+ },
+ visitMediaOut: function (mediaNode) {
+ this.allExtendsStack.length = this.allExtendsStack.length - 1;
+ },
+ visitDirective: function (directiveNode, visitArgs) {
+ directiveNode.allExtends = [];
+ this.allExtendsStack.push(directiveNode.allExtends);
+ },
+ visitDirectiveOut: function (directiveNode) {
+ this.allExtendsStack.length = this.allExtendsStack.length - 1;
+ }
+};
+
+var ProcessExtendsVisitor = function() {
+ this._visitor = new Visitor(this);
+};
+
+ProcessExtendsVisitor.prototype = {
+ run: function(root) {
+ var extendFinder = new ExtendFinderVisitor();
+ this.extendIndicies = {};
+ extendFinder.run(root);
+ if (!extendFinder.foundExtends) { return root; }
+ root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
+ this.allExtendsStack = [root.allExtends];
+ var newRoot = this._visitor.visit(root);
+ this.checkExtendsForNonMatched(root.allExtends);
+ return newRoot;
+ },
+ checkExtendsForNonMatched: function(extendList) {
+ var indicies = this.extendIndicies;
+ extendList.filter(function(extend) {
+ return !extend.hasFoundMatches && extend.parent_ids.length == 1;
+ }).forEach(function(extend) {
+ var selector = "_unknown_";
+ try {
+ selector = extend.selector.toCSS({});
+ }
+ catch(_) {}
+
+ if (!indicies[extend.index + ' ' + selector]) {
+ indicies[extend.index + ' ' + selector] = true;
+ logger.warn("extend '" + selector + "' has no matches");
+ }
+ });
+ },
+ doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
+ //
+ // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering
+ // and pasting the selector we would do normally, but we are also adding an extend with the same target selector
+ // this means this new extend can then go and alter other extends
+ //
+ // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
+ // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already
+ // processed if we look at each selector at a time, as is done in visitRuleset
+
+ var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath,
+ extend, targetExtend, newExtend;
+
+ iterationCount = iterationCount || 0;
+
+ //loop through comparing every extend with every target extend.
+ // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
+ // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
+ // and the second is the target.
+ // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
+ // case when processing media queries
+ for (extendIndex = 0; extendIndex < extendsList.length; extendIndex++) {
+ for (targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++) {
+
+ extend = extendsList[extendIndex];
+ targetExtend = extendsListTarget[targetExtendIndex];
+
+ // look for circular references
+ if ( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ) { continue; }
+
+ // find a match in the target extends self selector (the bit before :extend)
+ selectorPath = [targetExtend.selfSelectors[0]];
+ matches = extendVisitor.findMatch(extend, selectorPath);
+
+ if (matches.length) {
+
+ extend.hasFoundMatches = true;
+
+ // we found a match, so for each self selector..
+ extend.selfSelectors.forEach(function(selfSelector) {
+
+ // process the extend as usual
+ newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector);
+
+ // but now we create a new extend from it
+ newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0);
+ newExtend.selfSelectors = newSelector;
+
+ // add the extend onto the list of extends for that selector
+ newSelector[newSelector.length - 1].extendList = [newExtend];
+
+ // record that we need to add it.
+ extendsToAdd.push(newExtend);
+ newExtend.ruleset = targetExtend.ruleset;
+
+ //remember its parents for circular references
+ newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids);
+
+ // only process the selector once.. if we have :extend(.a,.b) then multiple
+ // extends will look at the same selector path, so when extending
+ // we know that any others will be duplicates in terms of what is added to the css
+ if (targetExtend.firstExtendOnThisSelectorPath) {
+ newExtend.firstExtendOnThisSelectorPath = true;
+ targetExtend.ruleset.paths.push(newSelector);
+ }
+ });
+ }
+ }
+ }
+
+ if (extendsToAdd.length) {
+ // try to detect circular references to stop a stack overflow.
+ // may no longer be needed.
+ this.extendChainCount++;
+ if (iterationCount > 100) {
+ var selectorOne = "{unable to calculate}";
+ var selectorTwo = "{unable to calculate}";
+ try
+ {
+ selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
+ selectorTwo = extendsToAdd[0].selector.toCSS();
+ }
+ catch(e) {}
+ throw { message: "extend circular reference detected. One of the circular extends is currently:" +
+ selectorOne + ":extend(" + selectorTwo + ")"};
+ }
+
+ // now process the new extends on the existing rules so that we can handle a extending b extending c extending
+ // d extending e...
+ return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount + 1));
+ } else {
+ return extendsToAdd;
+ }
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitSelector: function (selectorNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitRuleset: function (rulesetNode, visitArgs) {
+ if (rulesetNode.root) {
+ return;
+ }
+ var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length - 1],
+ selectorsToAdd = [], extendVisitor = this, selectorPath;
+
+ // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
+
+ for (extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
+ for (pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
+ selectorPath = rulesetNode.paths[pathIndex];
+
+ // extending extends happens initially, before the main pass
+ if (rulesetNode.extendOnEveryPath) { continue; }
+ var extendList = selectorPath[selectorPath.length - 1].extendList;
+ if (extendList && extendList.length) { continue; }
+
+ matches = this.findMatch(allExtends[extendIndex], selectorPath);
+
+ if (matches.length) {
+ allExtends[extendIndex].hasFoundMatches = true;
+
+ allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
+ selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
+ });
+ }
+ }
+ }
+ rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
+ },
+ findMatch: function (extend, haystackSelectorPath) {
+ //
+ // look through the haystack selector path to try and find the needle - extend.selector
+ // returns an array of selector matches that can then be replaced
+ //
+ var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement,
+ targetCombinator, i,
+ extendVisitor = this,
+ needleElements = extend.selector.elements,
+ potentialMatches = [], potentialMatch, matches = [];
+
+ // loop through the haystack elements
+ for (haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
+ hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
+
+ for (hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
+
+ haystackElement = hackstackSelector.elements[hackstackElementIndex];
+
+ // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
+ if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
+ potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0,
+ initialCombinator: haystackElement.combinator});
+ }
+
+ for (i = 0; i < potentialMatches.length; i++) {
+ potentialMatch = potentialMatches[i];
+
+ // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
+ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to
+ // work out what the resulting combinator will be
+ targetCombinator = haystackElement.combinator.value;
+ if (targetCombinator === '' && hackstackElementIndex === 0) {
+ targetCombinator = ' ';
+ }
+
+ // if we don't match, null our match to indicate failure
+ if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) ||
+ (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
+ potentialMatch = null;
+ } else {
+ potentialMatch.matched++;
+ }
+
+ // if we are still valid and have finished, test whether we have elements after and whether these are allowed
+ if (potentialMatch) {
+ potentialMatch.finished = potentialMatch.matched === needleElements.length;
+ if (potentialMatch.finished &&
+ (!extend.allowAfter &&
+ (hackstackElementIndex + 1 < hackstackSelector.elements.length || haystackSelectorIndex + 1 < haystackSelectorPath.length))) {
+ potentialMatch = null;
+ }
+ }
+ // if null we remove, if not, we are still valid, so either push as a valid match or continue
+ if (potentialMatch) {
+ if (potentialMatch.finished) {
+ potentialMatch.length = needleElements.length;
+ potentialMatch.endPathIndex = haystackSelectorIndex;
+ potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
+ potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
+ matches.push(potentialMatch);
+ }
+ } else {
+ potentialMatches.splice(i, 1);
+ i--;
+ }
+ }
+ }
+ }
+ return matches;
+ },
+ isElementValuesEqual: function(elementValue1, elementValue2) {
+ if (typeof elementValue1 === "string" || typeof elementValue2 === "string") {
+ return elementValue1 === elementValue2;
+ }
+ if (elementValue1 instanceof tree.Attribute) {
+ if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) {
+ return false;
+ }
+ if (!elementValue1.value || !elementValue2.value) {
+ if (elementValue1.value || elementValue2.value) {
+ return false;
+ }
+ return true;
+ }
+ elementValue1 = elementValue1.value.value || elementValue1.value;
+ elementValue2 = elementValue2.value.value || elementValue2.value;
+ return elementValue1 === elementValue2;
+ }
+ elementValue1 = elementValue1.value;
+ elementValue2 = elementValue2.value;
+ if (elementValue1 instanceof tree.Selector) {
+ if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
+ return false;
+ }
+ for (var i = 0; i < elementValue1.elements.length; i++) {
+ if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
+ if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
+ return false;
+ }
+ }
+ if (!this.isElementValuesEqual(elementValue1.elements[i].value, elementValue2.elements[i].value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ },
+ extendSelector:function (matches, selectorPath, replacementSelector) {
+
+ //for a set of matches, replace each match with the replacement selector
+
+ var currentSelectorPathIndex = 0,
+ currentSelectorPathElementIndex = 0,
+ path = [],
+ matchIndex,
+ selector,
+ firstElement,
+ match,
+ newElements;
+
+ for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
+ match = matches[matchIndex];
+ selector = selectorPath[match.pathIndex];
+ firstElement = new tree.Element(
+ match.initialCombinator,
+ replacementSelector.elements[0].value,
+ replacementSelector.elements[0].index,
+ replacementSelector.elements[0].currentFileInfo
+ );
+
+ if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
+ path[path.length - 1].elements = path[path.length - 1]
+ .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
+ currentSelectorPathElementIndex = 0;
+ currentSelectorPathIndex++;
+ }
+
+ newElements = selector.elements
+ .slice(currentSelectorPathElementIndex, match.index)
+ .concat([firstElement])
+ .concat(replacementSelector.elements.slice(1));
+
+ if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) {
+ path[path.length - 1].elements =
+ path[path.length - 1].elements.concat(newElements);
+ } else {
+ path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
+
+ path.push(new tree.Selector(
+ newElements
+ ));
+ }
+ currentSelectorPathIndex = match.endPathIndex;
+ currentSelectorPathElementIndex = match.endPathElementIndex;
+ if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) {
+ currentSelectorPathElementIndex = 0;
+ currentSelectorPathIndex++;
+ }
+ }
+
+ if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
+ path[path.length - 1].elements = path[path.length - 1]
+ .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
+ currentSelectorPathIndex++;
+ }
+
+ path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
+
+ return path;
+ },
+ visitRulesetOut: function (rulesetNode) {
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
+ newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
+ this.allExtendsStack.push(newAllExtends);
+ },
+ visitMediaOut: function (mediaNode) {
+ var lastIndex = this.allExtendsStack.length - 1;
+ this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
+ this.allExtendsStack.length = lastIndex;
+ },
+ visitDirective: function (directiveNode, visitArgs) {
+ var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
+ newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
+ this.allExtendsStack.push(newAllExtends);
+ },
+ visitDirectiveOut: function (directiveNode) {
+ var lastIndex = this.allExtendsStack.length - 1;
+ this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
+ this.allExtendsStack.length = lastIndex;
+ }
+};
+
+module.exports = ProcessExtendsVisitor;
--- /dev/null
+function ImportSequencer(onSequencerEmpty) {
+ this.imports = [];
+ this.variableImports = [];
+ this._onSequencerEmpty = onSequencerEmpty;
+ this._currentDepth = 0;
+}
+
+ImportSequencer.prototype.addImport = function(callback) {
+ var importSequencer = this,
+ importItem = {
+ callback: callback,
+ args: null,
+ isReady: false
+ };
+ this.imports.push(importItem);
+ return function() {
+ importItem.args = Array.prototype.slice.call(arguments, 0);
+ importItem.isReady = true;
+ importSequencer.tryRun();
+ };
+};
+
+ImportSequencer.prototype.addVariableImport = function(callback) {
+ this.variableImports.push(callback);
+};
+
+ImportSequencer.prototype.tryRun = function() {
+ this._currentDepth++;
+ try {
+ while (true) {
+ while (this.imports.length > 0) {
+ var importItem = this.imports[0];
+ if (!importItem.isReady) {
+ return;
+ }
+ this.imports = this.imports.slice(1);
+ importItem.callback.apply(null, importItem.args);
+ }
+ if (this.variableImports.length === 0) {
+ break;
+ }
+ var variableImport = this.variableImports[0];
+ this.variableImports = this.variableImports.slice(1);
+ variableImport();
+ }
+ } finally {
+ this._currentDepth--;
+ }
+ if (this._currentDepth === 0 && this._onSequencerEmpty) {
+ this._onSequencerEmpty();
+ }
+};
+
+module.exports = ImportSequencer;
--- /dev/null
+var contexts = require("../contexts"),
+ Visitor = require("./visitor"),
+ ImportSequencer = require("./import-sequencer");
+
+var ImportVisitor = function(importer, finish) {
+
+ this._visitor = new Visitor(this);
+ this._importer = importer;
+ this._finish = finish;
+ this.context = new contexts.Eval();
+ this.importCount = 0;
+ this.onceFileDetectionMap = {};
+ this.recursionDetector = {};
+ this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this));
+};
+
+ImportVisitor.prototype = {
+ isReplacing: false,
+ run: function (root) {
+ try {
+ // process the contents
+ this._visitor.visit(root);
+ }
+ catch(e) {
+ this.error = e;
+ }
+
+ this.isFinished = true;
+ this._sequencer.tryRun();
+ },
+ _onSequencerEmpty: function() {
+ if (!this.isFinished) {
+ return;
+ }
+ this._finish(this.error);
+ },
+ visitImport: function (importNode, visitArgs) {
+ var inlineCSS = importNode.options.inline;
+
+ if (!importNode.css || inlineCSS) {
+
+ var context = new contexts.Eval(this.context, this.context.frames.slice(0));
+ var importParent = context.frames[0];
+
+ this.importCount++;
+ if (importNode.isVariableImport()) {
+ this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent));
+ } else {
+ this.processImportNode(importNode, context, importParent);
+ }
+ }
+ visitArgs.visitDeeper = false;
+ },
+ processImportNode: function(importNode, context, importParent) {
+ var evaldImportNode,
+ inlineCSS = importNode.options.inline;
+
+ try {
+ evaldImportNode = importNode.evalForImport(context);
+ } catch(e) {
+ if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
+ // attempt to eval properly and treat as css
+ importNode.css = true;
+ // if that fails, this error will be thrown
+ importNode.error = e;
+ }
+
+ if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
+
+ if (evaldImportNode.options.multiple) {
+ context.importMultiple = true;
+ }
+
+ // try appending if we haven't determined if it is css or not
+ var tryAppendLessExtension = evaldImportNode.css === undefined;
+
+ for (var i = 0; i < importParent.rules.length; i++) {
+ if (importParent.rules[i] === importNode) {
+ importParent.rules[i] = evaldImportNode;
+ break;
+ }
+ }
+
+ var onImported = this.onImported.bind(this, evaldImportNode, context),
+ sequencedOnImported = this._sequencer.addImport(onImported);
+
+ this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo,
+ evaldImportNode.options, sequencedOnImported);
+ } else {
+ this.importCount--;
+ if (this.isFinished) {
+ this._sequencer.tryRun();
+ }
+ }
+ },
+ onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
+ if (e) {
+ if (!e.filename) {
+ e.index = importNode.index; e.filename = importNode.currentFileInfo.filename;
+ }
+ this.error = e;
+ }
+
+ var importVisitor = this,
+ inlineCSS = importNode.options.inline,
+ duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
+
+ if (!context.importMultiple) {
+ if (duplicateImport) {
+ importNode.skip = true;
+ } else {
+ importNode.skip = function() {
+ if (fullPath in importVisitor.onceFileDetectionMap) {
+ return true;
+ }
+ importVisitor.onceFileDetectionMap[fullPath] = true;
+ return false;
+ };
+ }
+ }
+
+ if (root) {
+ importNode.root = root;
+ importNode.importedFilename = fullPath;
+
+ if (!inlineCSS && (context.importMultiple || !duplicateImport)) {
+ importVisitor.recursionDetector[fullPath] = true;
+
+ var oldContext = this.context;
+ this.context = context;
+ try {
+ this._visitor.visit(root);
+ } catch (e) {
+ this.error = e;
+ }
+ this.context = oldContext;
+ }
+ }
+
+ importVisitor.importCount--;
+
+ if (importVisitor.isFinished) {
+ importVisitor._sequencer.tryRun();
+ }
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitDirective: function (directiveNode, visitArgs) {
+ this.context.frames.unshift(directiveNode);
+ },
+ visitDirectiveOut: function (directiveNode) {
+ this.context.frames.shift();
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ this.context.frames.unshift(mixinDefinitionNode);
+ },
+ visitMixinDefinitionOut: function (mixinDefinitionNode) {
+ this.context.frames.shift();
+ },
+ visitRuleset: function (rulesetNode, visitArgs) {
+ this.context.frames.unshift(rulesetNode);
+ },
+ visitRulesetOut: function (rulesetNode) {
+ this.context.frames.shift();
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ this.context.frames.unshift(mediaNode.rules[0]);
+ },
+ visitMediaOut: function (mediaNode) {
+ this.context.frames.shift();
+ }
+};
+module.exports = ImportVisitor;
--- /dev/null
+var visitors = {
+ Visitor: require("./visitor"),
+ ImportVisitor: require('./import-visitor'),
+ ExtendVisitor: require('./extend-visitor'),
+ JoinSelectorVisitor: require('./join-selector-visitor'),
+ ToCSSVisitor: require('./to-css-visitor')
+};
+
+module.exports = visitors;
--- /dev/null
+var Visitor = require("./visitor");
+
+var JoinSelectorVisitor = function() {
+ this.contexts = [[]];
+ this._visitor = new Visitor(this);
+};
+
+JoinSelectorVisitor.prototype = {
+ run: function (root) {
+ return this._visitor.visit(root);
+ },
+ visitRule: function (ruleNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+ visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
+ visitArgs.visitDeeper = false;
+ },
+
+ visitRuleset: function (rulesetNode, visitArgs) {
+ var context = this.contexts[this.contexts.length - 1],
+ paths = [], selectors;
+
+ this.contexts.push(paths);
+
+ if (! rulesetNode.root) {
+ selectors = rulesetNode.selectors;
+ if (selectors) {
+ selectors = selectors.filter(function(selector) { return selector.getIsOutput(); });
+ rulesetNode.selectors = selectors.length ? selectors : (selectors = null);
+ if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); }
+ }
+ if (!selectors) { rulesetNode.rules = null; }
+ rulesetNode.paths = paths;
+ }
+ },
+ visitRulesetOut: function (rulesetNode) {
+ this.contexts.length = this.contexts.length - 1;
+ },
+ visitMedia: function (mediaNode, visitArgs) {
+ var context = this.contexts[this.contexts.length - 1];
+ mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
+ }
+};
+
+module.exports = JoinSelectorVisitor;
--- /dev/null
+var tree = require("../tree"),
+ Visitor = require("./visitor");
+
+var ToCSSVisitor = function(context) {
+ this._visitor = new Visitor(this);
+ this._context = context;
+};
+
+ToCSSVisitor.prototype = {
+ isReplacing: true,
+ run: function (root) {
+ return this._visitor.visit(root);
+ },
+
+ visitRule: function (ruleNode, visitArgs) {
+ if (ruleNode.variable) {
+ return;
+ }
+ return ruleNode;
+ },
+
+ visitMixinDefinition: function (mixinNode, visitArgs) {
+ // mixin definitions do not get eval'd - this means they keep state
+ // so we have to clear that state here so it isn't used if toCSS is called twice
+ mixinNode.frames = [];
+ },
+
+ visitExtend: function (extendNode, visitArgs) {
+ },
+
+ visitComment: function (commentNode, visitArgs) {
+ if (commentNode.isSilent(this._context)) {
+ return;
+ }
+ return commentNode;
+ },
+
+ visitMedia: function(mediaNode, visitArgs) {
+ mediaNode.accept(this._visitor);
+ visitArgs.visitDeeper = false;
+
+ if (!mediaNode.rules.length) {
+ return;
+ }
+ return mediaNode;
+ },
+
+ visitDirective: function(directiveNode, visitArgs) {
+ if (directiveNode.name === "@charset") {
+ if (!directiveNode.getIsReferenced()) {
+ return;
+ }
+ // Only output the debug info together with subsequent @charset definitions
+ // a comment (or @media statement) before the actual @charset directive would
+ // be considered illegal css as it has to be on the first line
+ if (this.charset) {
+ if (directiveNode.debugInfo) {
+ var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "") + " */\n");
+ comment.debugInfo = directiveNode.debugInfo;
+ return this._visitor.visit(comment);
+ }
+ return;
+ }
+ this.charset = true;
+ }
+ if (directiveNode.rules && directiveNode.rules.rules) {
+ this._mergeRules(directiveNode.rules.rules);
+ //process childs
+ directiveNode.accept(this._visitor);
+ visitArgs.visitDeeper = false;
+
+ // the directive was directly referenced and therefore needs to be shown in the output
+ if (directiveNode.getIsReferenced()) {
+ return directiveNode;
+ }
+
+ if (!directiveNode.rules.rules) {
+ return ;
+ }
+
+ //the directive was not directly referenced
+ for (var r = 0; r < directiveNode.rules.rules.length; r++) {
+ var rule = directiveNode.rules.rules[r];
+ if (rule.getIsReferenced && rule.getIsReferenced()) {
+ //the directive contains something that was referenced (likely by extend)
+ //therefore it needs to be shown in output too
+
+ //marking as referenced in case the directive is stored inside another directive
+ directiveNode.markReferenced();
+ return directiveNode;
+ }
+ }
+ //The directive was not directly referenced and does not contain anything that
+ //was referenced. Therefore it must not be shown in output.
+ return ;
+ } else {
+ if (!directiveNode.getIsReferenced()) {
+ return;
+ }
+ }
+ return directiveNode;
+ },
+
+ checkPropertiesInRoot: function(rules) {
+ var ruleNode;
+ for (var i = 0; i < rules.length; i++) {
+ ruleNode = rules[i];
+ if (ruleNode instanceof tree.Rule && !ruleNode.variable) {
+ throw { message: "properties must be inside selector blocks, they cannot be in the root.",
+ index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null};
+ }
+ }
+ },
+
+ visitRuleset: function (rulesetNode, visitArgs) {
+ var rule, rulesets = [];
+ if (rulesetNode.firstRoot) {
+ this.checkPropertiesInRoot(rulesetNode.rules);
+ }
+ if (! rulesetNode.root) {
+ if (rulesetNode.paths) {
+ rulesetNode.paths = rulesetNode.paths
+ .filter(function(p) {
+ var i;
+ if (p[0].elements[0].combinator.value === ' ') {
+ p[0].elements[0].combinator = new(tree.Combinator)('');
+ }
+ for (i = 0; i < p.length; i++) {
+ if (p[i].getIsReferenced() && p[i].getIsOutput()) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ // Compile rules and rulesets
+ var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0;
+ for (var i = 0; i < nodeRuleCnt; ) {
+ rule = nodeRules[i];
+ if (rule && rule.rules) {
+ // visit because we are moving them out from being a child
+ rulesets.push(this._visitor.visit(rule));
+ nodeRules.splice(i, 1);
+ nodeRuleCnt--;
+ continue;
+ }
+ i++;
+ }
+ // accept the visitor to remove rules and refactor itself
+ // then we can decide now whether we want it or not
+ if (nodeRuleCnt > 0) {
+ rulesetNode.accept(this._visitor);
+ } else {
+ rulesetNode.rules = null;
+ }
+ visitArgs.visitDeeper = false;
+
+ nodeRules = rulesetNode.rules;
+ if (nodeRules) {
+ this._mergeRules(nodeRules);
+ nodeRules = rulesetNode.rules;
+ }
+ if (nodeRules) {
+ this._removeDuplicateRules(nodeRules);
+ nodeRules = rulesetNode.rules;
+ }
+
+ // now decide whether we keep the ruleset
+ if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) {
+ rulesets.splice(0, 0, rulesetNode);
+ }
+ } else {
+ rulesetNode.accept(this._visitor);
+ visitArgs.visitDeeper = false;
+ if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) {
+ rulesets.splice(0, 0, rulesetNode);
+ }
+ }
+ if (rulesets.length === 1) {
+ return rulesets[0];
+ }
+ return rulesets;
+ },
+
+ _removeDuplicateRules: function(rules) {
+ if (!rules) { return; }
+
+ // remove duplicates
+ var ruleCache = {},
+ ruleList, rule, i;
+
+ for (i = rules.length - 1; i >= 0 ; i--) {
+ rule = rules[i];
+ if (rule instanceof tree.Rule) {
+ if (!ruleCache[rule.name]) {
+ ruleCache[rule.name] = rule;
+ } else {
+ ruleList = ruleCache[rule.name];
+ if (ruleList instanceof tree.Rule) {
+ ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)];
+ }
+ var ruleCSS = rule.toCSS(this._context);
+ if (ruleList.indexOf(ruleCSS) !== -1) {
+ rules.splice(i, 1);
+ } else {
+ ruleList.push(ruleCSS);
+ }
+ }
+ }
+ }
+ },
+
+ _mergeRules: function (rules) {
+ if (!rules) { return; }
+
+ var groups = {},
+ parts,
+ rule,
+ key;
+
+ for (var i = 0; i < rules.length; i++) {
+ rule = rules[i];
+
+ if ((rule instanceof tree.Rule) && rule.merge) {
+ key = [rule.name,
+ rule.important ? "!" : ""].join(",");
+
+ if (!groups[key]) {
+ groups[key] = [];
+ } else {
+ rules.splice(i--, 1);
+ }
+
+ groups[key].push(rule);
+ }
+ }
+
+ Object.keys(groups).map(function (k) {
+
+ function toExpression(values) {
+ return new (tree.Expression)(values.map(function (p) {
+ return p.value;
+ }));
+ }
+
+ function toValue(values) {
+ return new (tree.Value)(values.map(function (p) {
+ return p;
+ }));
+ }
+
+ parts = groups[k];
+
+ if (parts.length > 1) {
+ rule = parts[0];
+ var spacedGroups = [];
+ var lastSpacedGroup = [];
+ parts.map(function (p) {
+ if (p.merge === "+") {
+ if (lastSpacedGroup.length > 0) {
+ spacedGroups.push(toExpression(lastSpacedGroup));
+ }
+ lastSpacedGroup = [];
+ }
+ lastSpacedGroup.push(p);
+ });
+ spacedGroups.push(toExpression(lastSpacedGroup));
+ rule.value = toValue(spacedGroups);
+ }
+ });
+ }
+};
+
+module.exports = ToCSSVisitor;
--- /dev/null
+var tree = require("../tree");
+
+var _visitArgs = { visitDeeper: true },
+ _hasIndexed = false;
+
+function _noop(node) {
+ return node;
+}
+
+function indexNodeTypes(parent, ticker) {
+ // add .typeIndex to tree node types for lookup table
+ var key, child;
+ for (key in parent) {
+ if (parent.hasOwnProperty(key)) {
+ child = parent[key];
+ switch (typeof child) {
+ case "function":
+ // ignore bound functions directly on tree which do not have a prototype
+ // or aren't nodes
+ if (child.prototype && child.prototype.type) {
+ child.prototype.typeIndex = ticker++;
+ }
+ break;
+ case "object":
+ ticker = indexNodeTypes(child, ticker);
+ break;
+ }
+ }
+ }
+ return ticker;
+}
+
+var Visitor = function(implementation) {
+ this._implementation = implementation;
+ this._visitFnCache = [];
+
+ if (!_hasIndexed) {
+ indexNodeTypes(tree, 1);
+ _hasIndexed = true;
+ }
+};
+
+Visitor.prototype = {
+ visit: function(node) {
+ if (!node) {
+ return node;
+ }
+
+ var nodeTypeIndex = node.typeIndex;
+ if (!nodeTypeIndex) {
+ return node;
+ }
+
+ var visitFnCache = this._visitFnCache,
+ impl = this._implementation,
+ aryIndx = nodeTypeIndex << 1,
+ outAryIndex = aryIndx | 1,
+ func = visitFnCache[aryIndx],
+ funcOut = visitFnCache[outAryIndex],
+ visitArgs = _visitArgs,
+ fnName;
+
+ visitArgs.visitDeeper = true;
+
+ if (!func) {
+ fnName = "visit" + node.type;
+ func = impl[fnName] || _noop;
+ funcOut = impl[fnName + "Out"] || _noop;
+ visitFnCache[aryIndx] = func;
+ visitFnCache[outAryIndex] = funcOut;
+ }
+
+ if (func !== _noop) {
+ var newNode = func.call(impl, node, visitArgs);
+ if (impl.isReplacing) {
+ node = newNode;
+ }
+ }
+
+ if (visitArgs.visitDeeper && node && node.accept) {
+ node.accept(this);
+ }
+
+ if (funcOut != _noop) {
+ funcOut.call(impl, node);
+ }
+
+ return node;
+ },
+ visitArray: function(nodes, nonReplacing) {
+ if (!nodes) {
+ return nodes;
+ }
+
+ var cnt = nodes.length, i;
+
+ // Non-replacing
+ if (nonReplacing || !this._implementation.isReplacing) {
+ for (i = 0; i < cnt; i++) {
+ this.visit(nodes[i]);
+ }
+ return nodes;
+ }
+
+ // Replacing
+ var out = [];
+ for (i = 0; i < cnt; i++) {
+ var evald = this.visit(nodes[i]);
+ if (evald === undefined) { continue; }
+ if (!evald.splice) {
+ out.push(evald);
+ } else if (evald.length) {
+ this.flatten(evald, out);
+ }
+ }
+ return out;
+ },
+ flatten: function(arr, out) {
+ if (!out) {
+ out = [];
+ }
+
+ var cnt, i, item,
+ nestedCnt, j, nestedItem;
+
+ for (i = 0, cnt = arr.length; i < cnt; i++) {
+ item = arr[i];
+ if (item === undefined) {
+ continue;
+ }
+ if (!item.splice) {
+ out.push(item);
+ continue;
+ }
+
+ for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) {
+ nestedItem = item[j];
+ if (nestedItem === undefined) {
+ continue;
+ }
+ if (!nestedItem.splice) {
+ out.push(nestedItem);
+ } else if (nestedItem.length) {
+ this.flatten(nestedItem, out);
+ }
+ }
+ }
+
+ return out;
+ }
+};
+module.exports = Visitor;
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/**
+ * Define a module along with a payload.
+ * @param {string} moduleName Name for the payload
+ * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
+ * @param {function} payload Function with (require, exports, module) params
+ */
+function define(moduleName, deps, payload) {
+ if (typeof moduleName != "string") {
+ throw new TypeError('Expected string, got: ' + moduleName);
+ }
+
+ if (arguments.length == 2) {
+ payload = deps;
+ }
+
+ if (moduleName in define.modules) {
+ throw new Error("Module already defined: " + moduleName);
+ }
+ define.modules[moduleName] = payload;
+};
+
+/**
+ * The global store of un-instantiated modules
+ */
+define.modules = {};
+
+
+/**
+ * We invoke require() in the context of a Domain so we can have multiple
+ * sets of modules running separate from each other.
+ * This contrasts with JSMs which are singletons, Domains allows us to
+ * optionally load a CommonJS module twice with separate data each time.
+ * Perhaps you want 2 command lines with a different set of commands in each,
+ * for example.
+ */
+function Domain() {
+ this.modules = {};
+ this._currentModule = null;
+}
+
+(function () {
+
+ /**
+ * Lookup module names and resolve them by calling the definition function if
+ * needed.
+ * There are 2 ways to call this, either with an array of dependencies and a
+ * callback to call when the dependencies are found (which can happen
+ * asynchronously in an in-page context) or with a single string an no callback
+ * where the dependency is resolved synchronously and returned.
+ * The API is designed to be compatible with the CommonJS AMD spec and
+ * RequireJS.
+ * @param {string[]|string} deps A name, or names for the payload
+ * @param {function|undefined} callback Function to call when the dependencies
+ * are resolved
+ * @return {undefined|object} The module required or undefined for
+ * array/callback method
+ */
+ Domain.prototype.require = function(deps, callback) {
+ if (Array.isArray(deps)) {
+ var params = deps.map(function(dep) {
+ return this.lookup(dep);
+ }, this);
+ if (callback) {
+ callback.apply(null, params);
+ }
+ return undefined;
+ }
+ else {
+ return this.lookup(deps);
+ }
+ };
+
+ function normalize(path) {
+ var bits = path.split('/');
+ var i = 1;
+ while (i < bits.length) {
+ if (bits[i] === '..') {
+ bits.splice(i-1, 1);
+ } else if (bits[i] === '.') {
+ bits.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ return bits.join('/');
+ }
+
+ function join(a, b) {
+ a = a.trim();
+ b = b.trim();
+ if (/^\//.test(b)) {
+ return b;
+ } else {
+ return a.replace(/\/*$/, '/') + b;
+ }
+ }
+
+ function dirname(path) {
+ var bits = path.split('/');
+ bits.pop();
+ return bits.join('/');
+ }
+
+ /**
+ * Lookup module names and resolve them by calling the definition function if
+ * needed.
+ * @param {string} moduleName A name for the payload to lookup
+ * @return {object} The module specified by aModuleName or null if not found.
+ */
+ Domain.prototype.lookup = function(moduleName) {
+ if (/^\./.test(moduleName)) {
+ moduleName = normalize(join(dirname(this._currentModule), moduleName));
+ }
+
+ if (moduleName in this.modules) {
+ var module = this.modules[moduleName];
+ return module;
+ }
+
+ if (!(moduleName in define.modules)) {
+ throw new Error("Module not defined: " + moduleName);
+ }
+
+ var module = define.modules[moduleName];
+
+ if (typeof module == "function") {
+ var exports = {};
+ var previousModule = this._currentModule;
+ this._currentModule = moduleName;
+ module(this.require.bind(this), exports, { id: moduleName, uri: "" });
+ this._currentModule = previousModule;
+ module = exports;
+ }
+
+ // cache the resulting module object for next time
+ this.modules[moduleName] = module;
+
+ return module;
+ };
+
+}());
+
+define.Domain = Domain;
+define.globalDomain = new Domain();
+var require = define.globalDomain.require.bind(define.globalDomain);
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) {
+
+ var base64VLQ = require('./base64-vlq');
+ var util = require('./util');
+ var ArraySet = require('./array-set').ArraySet;
+
+ /**
+ * An instance of the SourceMapGenerator represents a source map which is
+ * being built incrementally. To create a new one, you must pass an object
+ * with the following properties:
+ *
+ * - file: The filename of the generated source.
+ * - sourceRoot: An optional root for all URLs in this source map.
+ */
+ function SourceMapGenerator(aArgs) {
+ this._file = util.getArg(aArgs, 'file');
+ this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+ this._sources = new ArraySet();
+ this._names = new ArraySet();
+ this._mappings = [];
+ this._sourcesContents = null;
+ }
+
+ SourceMapGenerator.prototype._version = 3;
+
+ /**
+ * Creates a new SourceMapGenerator based on a SourceMapConsumer
+ *
+ * @param aSourceMapConsumer The SourceMap.
+ */
+ SourceMapGenerator.fromSourceMap =
+ function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
+ var sourceRoot = aSourceMapConsumer.sourceRoot;
+ var generator = new SourceMapGenerator({
+ file: aSourceMapConsumer.file,
+ sourceRoot: sourceRoot
+ });
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ var newMapping = {
+ generated: {
+ line: mapping.generatedLine,
+ column: mapping.generatedColumn
+ }
+ };
+
+ if (mapping.source) {
+ newMapping.source = mapping.source;
+ if (sourceRoot) {
+ newMapping.source = util.relative(sourceRoot, newMapping.source);
+ }
+
+ newMapping.original = {
+ line: mapping.originalLine,
+ column: mapping.originalColumn
+ };
+
+ if (mapping.name) {
+ newMapping.name = mapping.name;
+ }
+ }
+
+ generator.addMapping(newMapping);
+ });
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content) {
+ generator.setSourceContent(sourceFile, content);
+ }
+ });
+ return generator;
+ };
+
+ /**
+ * Add a single mapping from original source line and column to the generated
+ * source's line and column for this source map being created. The mapping
+ * object should have the following properties:
+ *
+ * - generated: An object with the generated line and column positions.
+ * - original: An object with the original line and column positions.
+ * - source: The original source file (relative to the sourceRoot).
+ * - name: An optional original token name for this mapping.
+ */
+ SourceMapGenerator.prototype.addMapping =
+ function SourceMapGenerator_addMapping(aArgs) {
+ var generated = util.getArg(aArgs, 'generated');
+ var original = util.getArg(aArgs, 'original', null);
+ var source = util.getArg(aArgs, 'source', null);
+ var name = util.getArg(aArgs, 'name', null);
+
+ this._validateMapping(generated, original, source, name);
+
+ if (source && !this._sources.has(source)) {
+ this._sources.add(source);
+ }
+
+ if (name && !this._names.has(name)) {
+ this._names.add(name);
+ }
+
+ this._mappings.push({
+ generatedLine: generated.line,
+ generatedColumn: generated.column,
+ originalLine: original != null && original.line,
+ originalColumn: original != null && original.column,
+ source: source,
+ name: name
+ });
+ };
+
+ /**
+ * Set the source content for a source file.
+ */
+ SourceMapGenerator.prototype.setSourceContent =
+ function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
+ var source = aSourceFile;
+ if (this._sourceRoot) {
+ source = util.relative(this._sourceRoot, source);
+ }
+
+ if (aSourceContent !== null) {
+ // Add the source content to the _sourcesContents map.
+ // Create a new _sourcesContents map if the property is null.
+ if (!this._sourcesContents) {
+ this._sourcesContents = {};
+ }
+ this._sourcesContents[util.toSetString(source)] = aSourceContent;
+ } else {
+ // Remove the source file from the _sourcesContents map.
+ // If the _sourcesContents map is empty, set the property to null.
+ delete this._sourcesContents[util.toSetString(source)];
+ if (Object.keys(this._sourcesContents).length === 0) {
+ this._sourcesContents = null;
+ }
+ }
+ };
+
+ /**
+ * Applies the mappings of a sub-source-map for a specific source file to the
+ * source map being generated. Each mapping to the supplied source file is
+ * rewritten using the supplied source map. Note: The resolution for the
+ * resulting mappings is the minimium of this map and the supplied map.
+ *
+ * @param aSourceMapConsumer The source map to be applied.
+ * @param aSourceFile Optional. The filename of the source file.
+ * If omitted, SourceMapConsumer's file property will be used.
+ */
+ SourceMapGenerator.prototype.applySourceMap =
+ function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
+ // If aSourceFile is omitted, we will use the file property of the SourceMap
+ if (!aSourceFile) {
+ aSourceFile = aSourceMapConsumer.file;
+ }
+ var sourceRoot = this._sourceRoot;
+ // Make "aSourceFile" relative if an absolute Url is passed.
+ if (sourceRoot) {
+ aSourceFile = util.relative(sourceRoot, aSourceFile);
+ }
+ // Applying the SourceMap can add and remove items from the sources and
+ // the names array.
+ var newSources = new ArraySet();
+ var newNames = new ArraySet();
+
+ // Find mappings for the "aSourceFile"
+ this._mappings.forEach(function (mapping) {
+ if (mapping.source === aSourceFile && mapping.originalLine) {
+ // Check if it can be mapped by the source map, then update the mapping.
+ var original = aSourceMapConsumer.originalPositionFor({
+ line: mapping.originalLine,
+ column: mapping.originalColumn
+ });
+ if (original.source !== null) {
+ // Copy mapping
+ if (sourceRoot) {
+ mapping.source = util.relative(sourceRoot, original.source);
+ } else {
+ mapping.source = original.source;
+ }
+ mapping.originalLine = original.line;
+ mapping.originalColumn = original.column;
+ if (original.name !== null && mapping.name !== null) {
+ // Only use the identifier name if it's an identifier
+ // in both SourceMaps
+ mapping.name = original.name;
+ }
+ }
+ }
+
+ var source = mapping.source;
+ if (source && !newSources.has(source)) {
+ newSources.add(source);
+ }
+
+ var name = mapping.name;
+ if (name && !newNames.has(name)) {
+ newNames.add(name);
+ }
+
+ }, this);
+ this._sources = newSources;
+ this._names = newNames;
+
+ // Copy sourcesContents of applied map.
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content) {
+ if (sourceRoot) {
+ sourceFile = util.relative(sourceRoot, sourceFile);
+ }
+ this.setSourceContent(sourceFile, content);
+ }
+ }, this);
+ };
+
+ /**
+ * A mapping can have one of the three levels of data:
+ *
+ * 1. Just the generated position.
+ * 2. The Generated position, original position, and original source.
+ * 3. Generated and original position, original source, as well as a name
+ * token.
+ *
+ * To maintain consistency, we validate that any new mapping being added falls
+ * in to one of these categories.
+ */
+ SourceMapGenerator.prototype._validateMapping =
+ function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+ aName) {
+ if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+ && aGenerated.line > 0 && aGenerated.column >= 0
+ && !aOriginal && !aSource && !aName) {
+ // Case 1.
+ return;
+ }
+ else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+ && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+ && aGenerated.line > 0 && aGenerated.column >= 0
+ && aOriginal.line > 0 && aOriginal.column >= 0
+ && aSource) {
+ // Cases 2 and 3.
+ return;
+ }
+ else {
+ throw new Error('Invalid mapping: ' + JSON.stringify({
+ generated: aGenerated,
+ source: aSource,
+ original: aOriginal,
+ name: aName
+ }));
+ }
+ };
+
+ /**
+ * Serialize the accumulated mappings in to the stream of base 64 VLQs
+ * specified by the source map format.
+ */
+ SourceMapGenerator.prototype._serializeMappings =
+ function SourceMapGenerator_serializeMappings() {
+ var previousGeneratedColumn = 0;
+ var previousGeneratedLine = 1;
+ var previousOriginalColumn = 0;
+ var previousOriginalLine = 0;
+ var previousName = 0;
+ var previousSource = 0;
+ var result = '';
+ var mapping;
+
+ // The mappings must be guaranteed to be in sorted order before we start
+ // serializing them or else the generated line numbers (which are defined
+ // via the ';' separators) will be all messed up. Note: it might be more
+ // performant to maintain the sorting as we insert them, rather than as we
+ // serialize them, but the big O is the same either way.
+ this._mappings.sort(util.compareByGeneratedPositions);
+
+ for (var i = 0, len = this._mappings.length; i < len; i++) {
+ mapping = this._mappings[i];
+
+ if (mapping.generatedLine !== previousGeneratedLine) {
+ previousGeneratedColumn = 0;
+ while (mapping.generatedLine !== previousGeneratedLine) {
+ result += ';';
+ previousGeneratedLine++;
+ }
+ }
+ else {
+ if (i > 0) {
+ if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
+ continue;
+ }
+ result += ',';
+ }
+ }
+
+ result += base64VLQ.encode(mapping.generatedColumn
+ - previousGeneratedColumn);
+ previousGeneratedColumn = mapping.generatedColumn;
+
+ if (mapping.source) {
+ result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+ - previousSource);
+ previousSource = this._sources.indexOf(mapping.source);
+
+ // lines are stored 0-based in SourceMap spec version 3
+ result += base64VLQ.encode(mapping.originalLine - 1
+ - previousOriginalLine);
+ previousOriginalLine = mapping.originalLine - 1;
+
+ result += base64VLQ.encode(mapping.originalColumn
+ - previousOriginalColumn);
+ previousOriginalColumn = mapping.originalColumn;
+
+ if (mapping.name) {
+ result += base64VLQ.encode(this._names.indexOf(mapping.name)
+ - previousName);
+ previousName = this._names.indexOf(mapping.name);
+ }
+ }
+ }
+
+ return result;
+ };
+
+ SourceMapGenerator.prototype._generateSourcesContent =
+ function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
+ return aSources.map(function (source) {
+ if (!this._sourcesContents) {
+ return null;
+ }
+ if (aSourceRoot) {
+ source = util.relative(aSourceRoot, source);
+ }
+ var key = util.toSetString(source);
+ return Object.prototype.hasOwnProperty.call(this._sourcesContents,
+ key)
+ ? this._sourcesContents[key]
+ : null;
+ }, this);
+ };
+
+ /**
+ * Externalize the source map.
+ */
+ SourceMapGenerator.prototype.toJSON =
+ function SourceMapGenerator_toJSON() {
+ var map = {
+ version: this._version,
+ file: this._file,
+ sources: this._sources.toArray(),
+ names: this._names.toArray(),
+ mappings: this._serializeMappings()
+ };
+ if (this._sourceRoot) {
+ map.sourceRoot = this._sourceRoot;
+ }
+ if (this._sourcesContents) {
+ map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
+ }
+
+ return map;
+ };
+
+ /**
+ * Render the source map being generated to a string.
+ */
+ SourceMapGenerator.prototype.toString =
+ function SourceMapGenerator_toString() {
+ return JSON.stringify(this);
+ };
+
+ exports.SourceMapGenerator = SourceMapGenerator;
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. 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 Google 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ */
+define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) {
+
+ var base64 = require('./base64');
+
+ // A single base 64 digit can contain 6 bits of data. For the base 64 variable
+ // length quantities we use in the source map spec, the first bit is the sign,
+ // the next four bits are the actual value, and the 6th bit is the
+ // continuation bit. The continuation bit tells us whether there are more
+ // digits in this value following this digit.
+ //
+ // Continuation
+ // | Sign
+ // | |
+ // V V
+ // 101011
+
+ var VLQ_BASE_SHIFT = 5;
+
+ // binary: 100000
+ var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+ // binary: 011111
+ var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+ // binary: 100000
+ var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+ /**
+ * Converts from a two-complement value to a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+ * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+ */
+ function toVLQSigned(aValue) {
+ return aValue < 0
+ ? ((-aValue) << 1) + 1
+ : (aValue << 1) + 0;
+ }
+
+ /**
+ * Converts to a two-complement value from a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+ * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+ */
+ function fromVLQSigned(aValue) {
+ var isNegative = (aValue & 1) === 1;
+ var shifted = aValue >> 1;
+ return isNegative
+ ? -shifted
+ : shifted;
+ }
+
+ /**
+ * Returns the base 64 VLQ encoded value.
+ */
+ exports.encode = function base64VLQ_encode(aValue) {
+ var encoded = "";
+ var digit;
+
+ var vlq = toVLQSigned(aValue);
+
+ do {
+ digit = vlq & VLQ_BASE_MASK;
+ vlq >>>= VLQ_BASE_SHIFT;
+ if (vlq > 0) {
+ // There are still more digits in this value, so we must make sure the
+ // continuation bit is marked.
+ digit |= VLQ_CONTINUATION_BIT;
+ }
+ encoded += base64.encode(digit);
+ } while (vlq > 0);
+
+ return encoded;
+ };
+
+ /**
+ * Decodes the next base 64 VLQ value from the given string and returns the
+ * value and the rest of the string.
+ */
+ exports.decode = function base64VLQ_decode(aStr) {
+ var i = 0;
+ var strLen = aStr.length;
+ var result = 0;
+ var shift = 0;
+ var continuation, digit;
+
+ do {
+ if (i >= strLen) {
+ throw new Error("Expected more digits in base 64 VLQ value.");
+ }
+ digit = base64.decode(aStr.charAt(i++));
+ continuation = !!(digit & VLQ_CONTINUATION_BIT);
+ digit &= VLQ_BASE_MASK;
+ result = result + (digit << shift);
+ shift += VLQ_BASE_SHIFT;
+ } while (continuation);
+
+ return {
+ value: fromVLQSigned(result),
+ rest: aStr.slice(i)
+ };
+ };
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) {
+
+ var charToIntMap = {};
+ var intToCharMap = {};
+
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ .split('')
+ .forEach(function (ch, index) {
+ charToIntMap[ch] = index;
+ intToCharMap[index] = ch;
+ });
+
+ /**
+ * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+ */
+ exports.encode = function base64_encode(aNumber) {
+ if (aNumber in intToCharMap) {
+ return intToCharMap[aNumber];
+ }
+ throw new TypeError("Must be between 0 and 63: " + aNumber);
+ };
+
+ /**
+ * Decode a single base 64 digit to an integer.
+ */
+ exports.decode = function base64_decode(aChar) {
+ if (aChar in charToIntMap) {
+ return charToIntMap[aChar];
+ }
+ throw new TypeError("Not a valid base 64 digit: " + aChar);
+ };
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) {
+
+ /**
+ * This is a helper function for getting values from parameter/options
+ * objects.
+ *
+ * @param args The object we are extracting values from
+ * @param name The name of the property we are getting.
+ * @param defaultValue An optional value to return if the property is missing
+ * from the object. If this is not specified and the property is missing, an
+ * error will be thrown.
+ */
+ function getArg(aArgs, aName, aDefaultValue) {
+ if (aName in aArgs) {
+ return aArgs[aName];
+ } else if (arguments.length === 3) {
+ return aDefaultValue;
+ } else {
+ throw new Error('"' + aName + '" is a required argument.');
+ }
+ }
+ exports.getArg = getArg;
+
+ var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
+ var dataUrlRegexp = /^data:.+\,.+/;
+
+ function urlParse(aUrl) {
+ var match = aUrl.match(urlRegexp);
+ if (!match) {
+ return null;
+ }
+ return {
+ scheme: match[1],
+ auth: match[3],
+ host: match[4],
+ port: match[6],
+ path: match[7]
+ };
+ }
+ exports.urlParse = urlParse;
+
+ function urlGenerate(aParsedUrl) {
+ var url = aParsedUrl.scheme + "://";
+ if (aParsedUrl.auth) {
+ url += aParsedUrl.auth + "@"
+ }
+ if (aParsedUrl.host) {
+ url += aParsedUrl.host;
+ }
+ if (aParsedUrl.port) {
+ url += ":" + aParsedUrl.port
+ }
+ if (aParsedUrl.path) {
+ url += aParsedUrl.path;
+ }
+ return url;
+ }
+ exports.urlGenerate = urlGenerate;
+
+ function join(aRoot, aPath) {
+ var url;
+
+ if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) {
+ return aPath;
+ }
+
+ if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
+ url.path = aPath;
+ return urlGenerate(url);
+ }
+
+ return aRoot.replace(/\/$/, '') + '/' + aPath;
+ }
+ exports.join = join;
+
+ /**
+ * Because behavior goes wacky when you set `__proto__` on objects, we
+ * have to prefix all the strings in our set with an arbitrary character.
+ *
+ * See https://github.com/mozilla/source-map/pull/31 and
+ * https://github.com/mozilla/source-map/issues/30
+ *
+ * @param String aStr
+ */
+ function toSetString(aStr) {
+ return '$' + aStr;
+ }
+ exports.toSetString = toSetString;
+
+ function fromSetString(aStr) {
+ return aStr.substr(1);
+ }
+ exports.fromSetString = fromSetString;
+
+ function relative(aRoot, aPath) {
+ aRoot = aRoot.replace(/\/$/, '');
+
+ var url = urlParse(aRoot);
+ if (aPath.charAt(0) == "/" && url && url.path == "/") {
+ return aPath.slice(1);
+ }
+
+ return aPath.indexOf(aRoot + '/') === 0
+ ? aPath.substr(aRoot.length + 1)
+ : aPath;
+ }
+ exports.relative = relative;
+
+ function strcmp(aStr1, aStr2) {
+ var s1 = aStr1 || "";
+ var s2 = aStr2 || "";
+ return (s1 > s2) - (s1 < s2);
+ }
+
+ /**
+ * Comparator between two mappings where the original positions are compared.
+ *
+ * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+ * mappings with the same original source/line/column, but different generated
+ * line and column the same. Useful when searching for a mapping with a
+ * stubbed out mapping.
+ */
+ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
+ var cmp;
+
+ cmp = strcmp(mappingA.source, mappingB.source);
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalLine - mappingB.originalLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalColumn - mappingB.originalColumn;
+ if (cmp || onlyCompareOriginal) {
+ return cmp;
+ }
+
+ cmp = strcmp(mappingA.name, mappingB.name);
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.generatedLine - mappingB.generatedLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ return mappingA.generatedColumn - mappingB.generatedColumn;
+ };
+ exports.compareByOriginalPositions = compareByOriginalPositions;
+
+ /**
+ * Comparator between two mappings where the generated positions are
+ * compared.
+ *
+ * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+ * mappings with the same generated line and column, but different
+ * source/name/original line and column the same. Useful when searching for a
+ * mapping with a stubbed out mapping.
+ */
+ function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
+ var cmp;
+
+ cmp = mappingA.generatedLine - mappingB.generatedLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+ if (cmp || onlyCompareGenerated) {
+ return cmp;
+ }
+
+ cmp = strcmp(mappingA.source, mappingB.source);
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalLine - mappingB.originalLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalColumn - mappingB.originalColumn;
+ if (cmp) {
+ return cmp;
+ }
+
+ return strcmp(mappingA.name, mappingB.name);
+ };
+ exports.compareByGeneratedPositions = compareByGeneratedPositions;
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) {
+
+ var util = require('./util');
+
+ /**
+ * A data structure which is a combination of an array and a set. Adding a new
+ * member is O(1), testing for membership is O(1), and finding the index of an
+ * element is O(1). Removing elements from the set is not supported. Only
+ * strings are supported for membership.
+ */
+ function ArraySet() {
+ this._array = [];
+ this._set = {};
+ }
+
+ /**
+ * Static method for creating ArraySet instances from an existing array.
+ */
+ ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
+ var set = new ArraySet();
+ for (var i = 0, len = aArray.length; i < len; i++) {
+ set.add(aArray[i], aAllowDuplicates);
+ }
+ return set;
+ };
+
+ /**
+ * Add the given string to this set.
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
+ var isDuplicate = this.has(aStr);
+ var idx = this._array.length;
+ if (!isDuplicate || aAllowDuplicates) {
+ this._array.push(aStr);
+ }
+ if (!isDuplicate) {
+ this._set[util.toSetString(aStr)] = idx;
+ }
+ };
+
+ /**
+ * Is the given string a member of this set?
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.has = function ArraySet_has(aStr) {
+ return Object.prototype.hasOwnProperty.call(this._set,
+ util.toSetString(aStr));
+ };
+
+ /**
+ * What is the index of the given string in the array?
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+ if (this.has(aStr)) {
+ return this._set[util.toSetString(aStr)];
+ }
+ throw new Error('"' + aStr + '" is not in the set.');
+ };
+
+ /**
+ * What is the element at the given index?
+ *
+ * @param Number aIdx
+ */
+ ArraySet.prototype.at = function ArraySet_at(aIdx) {
+ if (aIdx >= 0 && aIdx < this._array.length) {
+ return this._array[aIdx];
+ }
+ throw new Error('No element indexed by ' + aIdx);
+ };
+
+ /**
+ * Returns the array representation of this set (which has the proper indices
+ * indicated by indexOf). Note that this is a copy of the internal array used
+ * for storing the members so that no one can mess with internal state.
+ */
+ ArraySet.prototype.toArray = function ArraySet_toArray() {
+ return this._array.slice();
+ };
+
+ exports.ArraySet = ArraySet;
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) {
+
+ var util = require('./util');
+ var binarySearch = require('./binary-search');
+ var ArraySet = require('./array-set').ArraySet;
+ var base64VLQ = require('./base64-vlq');
+
+ /**
+ * A SourceMapConsumer instance represents a parsed source map which we can
+ * query for information about the original file positions by giving it a file
+ * position in the generated source.
+ *
+ * The only parameter is the raw source map (either as a JSON string, or
+ * already parsed to an object). According to the spec, source maps have the
+ * following attributes:
+ *
+ * - version: Which version of the source map spec this map is following.
+ * - sources: An array of URLs to the original source files.
+ * - names: An array of identifiers which can be referrenced by individual mappings.
+ * - sourceRoot: Optional. The URL root from which all sources are relative.
+ * - sourcesContent: Optional. An array of contents of the original source files.
+ * - mappings: A string of base64 VLQs which contain the actual mappings.
+ * - file: The generated file this source map is associated with.
+ *
+ * Here is an example source map, taken from the source map spec[0]:
+ *
+ * {
+ * version : 3,
+ * file: "out.js",
+ * sourceRoot : "",
+ * sources: ["foo.js", "bar.js"],
+ * names: ["src", "maps", "are", "fun"],
+ * mappings: "AA,AB;;ABCDE;"
+ * }
+ *
+ * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+ */
+ function SourceMapConsumer(aSourceMap) {
+ var sourceMap = aSourceMap;
+ if (typeof aSourceMap === 'string') {
+ sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+ }
+
+ var version = util.getArg(sourceMap, 'version');
+ var sources = util.getArg(sourceMap, 'sources');
+ // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
+ // requires the array) to play nice here.
+ var names = util.getArg(sourceMap, 'names', []);
+ var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+ var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
+ var mappings = util.getArg(sourceMap, 'mappings');
+ var file = util.getArg(sourceMap, 'file', null);
+
+ // Once again, Sass deviates from the spec and supplies the version as a
+ // string rather than a number, so we use loose equality checking here.
+ if (version != this._version) {
+ throw new Error('Unsupported version: ' + version);
+ }
+
+ // Pass `true` below to allow duplicate names and sources. While source maps
+ // are intended to be compressed and deduplicated, the TypeScript compiler
+ // sometimes generates source maps with duplicates in them. See Github issue
+ // #72 and bugzil.la/889492.
+ this._names = ArraySet.fromArray(names, true);
+ this._sources = ArraySet.fromArray(sources, true);
+
+ this.sourceRoot = sourceRoot;
+ this.sourcesContent = sourcesContent;
+ this._mappings = mappings;
+ this.file = file;
+ }
+
+ /**
+ * Create a SourceMapConsumer from a SourceMapGenerator.
+ *
+ * @param SourceMapGenerator aSourceMap
+ * The source map that will be consumed.
+ * @returns SourceMapConsumer
+ */
+ SourceMapConsumer.fromSourceMap =
+ function SourceMapConsumer_fromSourceMap(aSourceMap) {
+ var smc = Object.create(SourceMapConsumer.prototype);
+
+ smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
+ smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
+ smc.sourceRoot = aSourceMap._sourceRoot;
+ smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
+ smc.sourceRoot);
+ smc.file = aSourceMap._file;
+
+ smc.__generatedMappings = aSourceMap._mappings.slice()
+ .sort(util.compareByGeneratedPositions);
+ smc.__originalMappings = aSourceMap._mappings.slice()
+ .sort(util.compareByOriginalPositions);
+
+ return smc;
+ };
+
+ /**
+ * The version of the source mapping spec that we are consuming.
+ */
+ SourceMapConsumer.prototype._version = 3;
+
+ /**
+ * The list of original sources.
+ */
+ Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
+ get: function () {
+ return this._sources.toArray().map(function (s) {
+ return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
+ }, this);
+ }
+ });
+
+ // `__generatedMappings` and `__originalMappings` are arrays that hold the
+ // parsed mapping coordinates from the source map's "mappings" attribute. They
+ // are lazily instantiated, accessed via the `_generatedMappings` and
+ // `_originalMappings` getters respectively, and we only parse the mappings
+ // and create these arrays once queried for a source location. We jump through
+ // these hoops because there can be many thousands of mappings, and parsing
+ // them is expensive, so we only want to do it if we must.
+ //
+ // Each object in the arrays is of the form:
+ //
+ // {
+ // generatedLine: The line number in the generated code,
+ // generatedColumn: The column number in the generated code,
+ // source: The path to the original source file that generated this
+ // chunk of code,
+ // originalLine: The line number in the original source that
+ // corresponds to this chunk of generated code,
+ // originalColumn: The column number in the original source that
+ // corresponds to this chunk of generated code,
+ // name: The name of the original symbol which generated this chunk of
+ // code.
+ // }
+ //
+ // All properties except for `generatedLine` and `generatedColumn` can be
+ // `null`.
+ //
+ // `_generatedMappings` is ordered by the generated positions.
+ //
+ // `_originalMappings` is ordered by the original positions.
+
+ SourceMapConsumer.prototype.__generatedMappings = null;
+ Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+ get: function () {
+ if (!this.__generatedMappings) {
+ this.__generatedMappings = [];
+ this.__originalMappings = [];
+ this._parseMappings(this._mappings, this.sourceRoot);
+ }
+
+ return this.__generatedMappings;
+ }
+ });
+
+ SourceMapConsumer.prototype.__originalMappings = null;
+ Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+ get: function () {
+ if (!this.__originalMappings) {
+ this.__generatedMappings = [];
+ this.__originalMappings = [];
+ this._parseMappings(this._mappings, this.sourceRoot);
+ }
+
+ return this.__originalMappings;
+ }
+ });
+
+ /**
+ * Parse the mappings in a string in to a data structure which we can easily
+ * query (the ordered arrays in the `this.__generatedMappings` and
+ * `this.__originalMappings` properties).
+ */
+ SourceMapConsumer.prototype._parseMappings =
+ function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+ var generatedLine = 1;
+ var previousGeneratedColumn = 0;
+ var previousOriginalLine = 0;
+ var previousOriginalColumn = 0;
+ var previousSource = 0;
+ var previousName = 0;
+ var mappingSeparator = /^[,;]/;
+ var str = aStr;
+ var mapping;
+ var temp;
+
+ while (str.length > 0) {
+ if (str.charAt(0) === ';') {
+ generatedLine++;
+ str = str.slice(1);
+ previousGeneratedColumn = 0;
+ }
+ else if (str.charAt(0) === ',') {
+ str = str.slice(1);
+ }
+ else {
+ mapping = {};
+ mapping.generatedLine = generatedLine;
+
+ // Generated column.
+ temp = base64VLQ.decode(str);
+ mapping.generatedColumn = previousGeneratedColumn + temp.value;
+ previousGeneratedColumn = mapping.generatedColumn;
+ str = temp.rest;
+
+ if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+ // Original source.
+ temp = base64VLQ.decode(str);
+ mapping.source = this._sources.at(previousSource + temp.value);
+ previousSource += temp.value;
+ str = temp.rest;
+ if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+ throw new Error('Found a source, but no line and column');
+ }
+
+ // Original line.
+ temp = base64VLQ.decode(str);
+ mapping.originalLine = previousOriginalLine + temp.value;
+ previousOriginalLine = mapping.originalLine;
+ // Lines are stored 0-based
+ mapping.originalLine += 1;
+ str = temp.rest;
+ if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+ throw new Error('Found a source and line, but no column');
+ }
+
+ // Original column.
+ temp = base64VLQ.decode(str);
+ mapping.originalColumn = previousOriginalColumn + temp.value;
+ previousOriginalColumn = mapping.originalColumn;
+ str = temp.rest;
+
+ if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+ // Original name.
+ temp = base64VLQ.decode(str);
+ mapping.name = this._names.at(previousName + temp.value);
+ previousName += temp.value;
+ str = temp.rest;
+ }
+ }
+
+ this.__generatedMappings.push(mapping);
+ if (typeof mapping.originalLine === 'number') {
+ this.__originalMappings.push(mapping);
+ }
+ }
+ }
+
+ this.__originalMappings.sort(util.compareByOriginalPositions);
+ };
+
+ /**
+ * Find the mapping that best matches the hypothetical "needle" mapping that
+ * we are searching for in the given "haystack" of mappings.
+ */
+ SourceMapConsumer.prototype._findMapping =
+ function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
+ aColumnName, aComparator) {
+ // To return the position we are searching for, we must first find the
+ // mapping for the given position and then return the opposite position it
+ // points to. Because the mappings are sorted, we can use binary search to
+ // find the best mapping.
+
+ if (aNeedle[aLineName] <= 0) {
+ throw new TypeError('Line must be greater than or equal to 1, got '
+ + aNeedle[aLineName]);
+ }
+ if (aNeedle[aColumnName] < 0) {
+ throw new TypeError('Column must be greater than or equal to 0, got '
+ + aNeedle[aColumnName]);
+ }
+
+ return binarySearch.search(aNeedle, aMappings, aComparator);
+ };
+
+ /**
+ * Returns the original source, line, and column information for the generated
+ * source's line and column positions provided. The only argument is an object
+ * with the following properties:
+ *
+ * - line: The line number in the generated source.
+ * - column: The column number in the generated source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - source: The original source file, or null.
+ * - line: The line number in the original source, or null.
+ * - column: The column number in the original source, or null.
+ * - name: The original identifier, or null.
+ */
+ SourceMapConsumer.prototype.originalPositionFor =
+ function SourceMapConsumer_originalPositionFor(aArgs) {
+ var needle = {
+ generatedLine: util.getArg(aArgs, 'line'),
+ generatedColumn: util.getArg(aArgs, 'column')
+ };
+
+ var mapping = this._findMapping(needle,
+ this._generatedMappings,
+ "generatedLine",
+ "generatedColumn",
+ util.compareByGeneratedPositions);
+
+ if (mapping) {
+ var source = util.getArg(mapping, 'source', null);
+ if (source && this.sourceRoot) {
+ source = util.join(this.sourceRoot, source);
+ }
+ return {
+ source: source,
+ line: util.getArg(mapping, 'originalLine', null),
+ column: util.getArg(mapping, 'originalColumn', null),
+ name: util.getArg(mapping, 'name', null)
+ };
+ }
+
+ return {
+ source: null,
+ line: null,
+ column: null,
+ name: null
+ };
+ };
+
+ /**
+ * Returns the original source content. The only argument is the url of the
+ * original source file. Returns null if no original source content is
+ * availible.
+ */
+ SourceMapConsumer.prototype.sourceContentFor =
+ function SourceMapConsumer_sourceContentFor(aSource) {
+ if (!this.sourcesContent) {
+ return null;
+ }
+
+ if (this.sourceRoot) {
+ aSource = util.relative(this.sourceRoot, aSource);
+ }
+
+ if (this._sources.has(aSource)) {
+ return this.sourcesContent[this._sources.indexOf(aSource)];
+ }
+
+ var url;
+ if (this.sourceRoot
+ && (url = util.urlParse(this.sourceRoot))) {
+ // XXX: file:// URIs and absolute paths lead to unexpected behavior for
+ // many users. We can help them out when they expect file:// URIs to
+ // behave like it would if they were running a local HTTP server. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
+ var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+ if (url.scheme == "file"
+ && this._sources.has(fileUriAbsPath)) {
+ return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
+ }
+
+ if ((!url.path || url.path == "/")
+ && this._sources.has("/" + aSource)) {
+ return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+ }
+ }
+
+ throw new Error('"' + aSource + '" is not in the SourceMap.');
+ };
+
+ /**
+ * Returns the generated line and column information for the original source,
+ * line, and column positions provided. The only argument is an object with
+ * the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source.
+ * - column: The column number in the original source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - line: The line number in the generated source, or null.
+ * - column: The column number in the generated source, or null.
+ */
+ SourceMapConsumer.prototype.generatedPositionFor =
+ function SourceMapConsumer_generatedPositionFor(aArgs) {
+ var needle = {
+ source: util.getArg(aArgs, 'source'),
+ originalLine: util.getArg(aArgs, 'line'),
+ originalColumn: util.getArg(aArgs, 'column')
+ };
+
+ if (this.sourceRoot) {
+ needle.source = util.relative(this.sourceRoot, needle.source);
+ }
+
+ var mapping = this._findMapping(needle,
+ this._originalMappings,
+ "originalLine",
+ "originalColumn",
+ util.compareByOriginalPositions);
+
+ if (mapping) {
+ return {
+ line: util.getArg(mapping, 'generatedLine', null),
+ column: util.getArg(mapping, 'generatedColumn', null)
+ };
+ }
+
+ return {
+ line: null,
+ column: null
+ };
+ };
+
+ SourceMapConsumer.GENERATED_ORDER = 1;
+ SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+ /**
+ * Iterate over each mapping between an original source/line/column and a
+ * generated line/column in this source map.
+ *
+ * @param Function aCallback
+ * The function that is called with each mapping.
+ * @param Object aContext
+ * Optional. If specified, this object will be the value of `this` every
+ * time that `aCallback` is called.
+ * @param aOrder
+ * Either `SourceMapConsumer.GENERATED_ORDER` or
+ * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+ * iterate over the mappings sorted by the generated file's line/column
+ * order or the original's source/line/column order, respectively. Defaults to
+ * `SourceMapConsumer.GENERATED_ORDER`.
+ */
+ SourceMapConsumer.prototype.eachMapping =
+ function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+ var context = aContext || null;
+ var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+ var mappings;
+ switch (order) {
+ case SourceMapConsumer.GENERATED_ORDER:
+ mappings = this._generatedMappings;
+ break;
+ case SourceMapConsumer.ORIGINAL_ORDER:
+ mappings = this._originalMappings;
+ break;
+ default:
+ throw new Error("Unknown order of iteration.");
+ }
+
+ var sourceRoot = this.sourceRoot;
+ mappings.map(function (mapping) {
+ var source = mapping.source;
+ if (source && sourceRoot) {
+ source = util.join(sourceRoot, source);
+ }
+ return {
+ source: source,
+ generatedLine: mapping.generatedLine,
+ generatedColumn: mapping.generatedColumn,
+ originalLine: mapping.originalLine,
+ originalColumn: mapping.originalColumn,
+ name: mapping.name
+ };
+ }).forEach(aCallback, context);
+ };
+
+ exports.SourceMapConsumer = SourceMapConsumer;
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) {
+
+ /**
+ * Recursive implementation of binary search.
+ *
+ * @param aLow Indices here and lower do not contain the needle.
+ * @param aHigh Indices here and higher do not contain the needle.
+ * @param aNeedle The element being searched for.
+ * @param aHaystack The non-empty array being searched.
+ * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+ */
+ function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
+ // This function terminates when one of the following is true:
+ //
+ // 1. We find the exact element we are looking for.
+ //
+ // 2. We did not find the exact element, but we can return the next
+ // closest element that is less than that element.
+ //
+ // 3. We did not find the exact element, and there is no next-closest
+ // element which is less than the one we are searching for, so we
+ // return null.
+ var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+ var cmp = aCompare(aNeedle, aHaystack[mid], true);
+ if (cmp === 0) {
+ // Found the element we are looking for.
+ return aHaystack[mid];
+ }
+ else if (cmp > 0) {
+ // aHaystack[mid] is greater than our needle.
+ if (aHigh - mid > 1) {
+ // The element is in the upper half.
+ return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
+ }
+ // We did not find an exact match, return the next closest one
+ // (termination case 2).
+ return aHaystack[mid];
+ }
+ else {
+ // aHaystack[mid] is less than our needle.
+ if (mid - aLow > 1) {
+ // The element is in the lower half.
+ return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
+ }
+ // The exact needle element was not found in this haystack. Determine if
+ // we are in termination case (2) or (3) and return the appropriate thing.
+ return aLow < 0
+ ? null
+ : aHaystack[aLow];
+ }
+ }
+
+ /**
+ * This is an implementation of binary search which will always try and return
+ * the next lowest value checked if there is no exact hit. This is because
+ * mappings between original and generated line/col pairs are single points,
+ * and there is an implicit region between each of them, so a miss just means
+ * that you aren't on the very start of a region.
+ *
+ * @param aNeedle The element you are looking for.
+ * @param aHaystack The array that is being searched.
+ * @param aCompare A function which takes the needle and an element in the
+ * array and returns -1, 0, or 1 depending on whether the needle is less
+ * than, equal to, or greater than the element, respectively.
+ */
+ exports.search = function search(aNeedle, aHaystack, aCompare) {
+ return aHaystack.length > 0
+ ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
+ : null;
+ };
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) {
+
+ var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
+ var util = require('./util');
+
+ /**
+ * SourceNodes provide a way to abstract over interpolating/concatenating
+ * snippets of generated JavaScript source code while maintaining the line and
+ * column information associated with the original source code.
+ *
+ * @param aLine The original line number.
+ * @param aColumn The original column number.
+ * @param aSource The original source's filename.
+ * @param aChunks Optional. An array of strings which are snippets of
+ * generated JS, or other SourceNodes.
+ * @param aName The original identifier.
+ */
+ function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
+ this.children = [];
+ this.sourceContents = {};
+ this.line = aLine === undefined ? null : aLine;
+ this.column = aColumn === undefined ? null : aColumn;
+ this.source = aSource === undefined ? null : aSource;
+ this.name = aName === undefined ? null : aName;
+ if (aChunks != null) this.add(aChunks);
+ }
+
+ /**
+ * Creates a SourceNode from generated code and a SourceMapConsumer.
+ *
+ * @param aGeneratedCode The generated code
+ * @param aSourceMapConsumer The SourceMap for the generated code
+ */
+ SourceNode.fromStringWithSourceMap =
+ function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
+ // The SourceNode we want to fill with the generated code
+ // and the SourceMap
+ var node = new SourceNode();
+
+ // The generated code
+ // Processed fragments are removed from this array.
+ var remainingLines = aGeneratedCode.split('\n');
+
+ // We need to remember the position of "remainingLines"
+ var lastGeneratedLine = 1, lastGeneratedColumn = 0;
+
+ // The generate SourceNodes we need a code range.
+ // To extract it current and last mapping is used.
+ // Here we store the last mapping.
+ var lastMapping = null;
+
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ if (lastMapping === null) {
+ // We add the generated code until the first mapping
+ // to the SourceNode without any mapping.
+ // Each line is added as separate string.
+ while (lastGeneratedLine < mapping.generatedLine) {
+ node.add(remainingLines.shift() + "\n");
+ lastGeneratedLine++;
+ }
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ var nextLine = remainingLines[0];
+ node.add(nextLine.substr(0, mapping.generatedColumn));
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
+ } else {
+ // We add the code from "lastMapping" to "mapping":
+ // First check if there is a new line in between.
+ if (lastGeneratedLine < mapping.generatedLine) {
+ var code = "";
+ // Associate full lines with "lastMapping"
+ do {
+ code += remainingLines.shift() + "\n";
+ lastGeneratedLine++;
+ lastGeneratedColumn = 0;
+ } while (lastGeneratedLine < mapping.generatedLine);
+ // When we reached the correct line, we add code until we
+ // reach the correct column too.
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ var nextLine = remainingLines[0];
+ code += nextLine.substr(0, mapping.generatedColumn);
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
+ // Create the SourceNode.
+ addMappingWithCode(lastMapping, code);
+ } else {
+ // There is no new line in between.
+ // Associate the code between "lastGeneratedColumn" and
+ // "mapping.generatedColumn" with "lastMapping"
+ var nextLine = remainingLines[0];
+ var code = nextLine.substr(0, mapping.generatedColumn -
+ lastGeneratedColumn);
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+ lastGeneratedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ addMappingWithCode(lastMapping, code);
+ }
+ }
+ lastMapping = mapping;
+ }, this);
+ // We have processed all mappings.
+ // Associate the remaining code in the current line with "lastMapping"
+ // and add the remaining lines without any mapping
+ addMappingWithCode(lastMapping, remainingLines.join("\n"));
+
+ // Copy sourcesContent into SourceNode
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content) {
+ node.setSourceContent(sourceFile, content);
+ }
+ });
+
+ return node;
+
+ function addMappingWithCode(mapping, code) {
+ if (mapping === null || mapping.source === undefined) {
+ node.add(code);
+ } else {
+ node.add(new SourceNode(mapping.originalLine,
+ mapping.originalColumn,
+ mapping.source,
+ code,
+ mapping.name));
+ }
+ }
+ };
+
+ /**
+ * Add a chunk of generated JS to this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ SourceNode.prototype.add = function SourceNode_add(aChunk) {
+ if (Array.isArray(aChunk)) {
+ aChunk.forEach(function (chunk) {
+ this.add(chunk);
+ }, this);
+ }
+ else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+ if (aChunk) {
+ this.children.push(aChunk);
+ }
+ }
+ else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+ );
+ }
+ return this;
+ };
+
+ /**
+ * Add a chunk of generated JS to the beginning of this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+ if (Array.isArray(aChunk)) {
+ for (var i = aChunk.length-1; i >= 0; i--) {
+ this.prepend(aChunk[i]);
+ }
+ }
+ else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+ this.children.unshift(aChunk);
+ }
+ else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+ );
+ }
+ return this;
+ };
+
+ /**
+ * Walk over the tree of JS snippets in this node and its children. The
+ * walking function is called once for each snippet of JS and is passed that
+ * snippet and the its original associated source's line/column location.
+ *
+ * @param aFn The traversal function.
+ */
+ SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+ var chunk;
+ for (var i = 0, len = this.children.length; i < len; i++) {
+ chunk = this.children[i];
+ if (chunk instanceof SourceNode) {
+ chunk.walk(aFn);
+ }
+ else {
+ if (chunk !== '') {
+ aFn(chunk, { source: this.source,
+ line: this.line,
+ column: this.column,
+ name: this.name });
+ }
+ }
+ }
+ };
+
+ /**
+ * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+ * each of `this.children`.
+ *
+ * @param aSep The separator.
+ */
+ SourceNode.prototype.join = function SourceNode_join(aSep) {
+ var newChildren;
+ var i;
+ var len = this.children.length;
+ if (len > 0) {
+ newChildren = [];
+ for (i = 0; i < len-1; i++) {
+ newChildren.push(this.children[i]);
+ newChildren.push(aSep);
+ }
+ newChildren.push(this.children[i]);
+ this.children = newChildren;
+ }
+ return this;
+ };
+
+ /**
+ * Call String.prototype.replace on the very right-most source snippet. Useful
+ * for trimming whitespace from the end of a source node, etc.
+ *
+ * @param aPattern The pattern to replace.
+ * @param aReplacement The thing to replace the pattern with.
+ */
+ SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+ var lastChild = this.children[this.children.length - 1];
+ if (lastChild instanceof SourceNode) {
+ lastChild.replaceRight(aPattern, aReplacement);
+ }
+ else if (typeof lastChild === 'string') {
+ this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
+ }
+ else {
+ this.children.push(''.replace(aPattern, aReplacement));
+ }
+ return this;
+ };
+
+ /**
+ * Set the source content for a source file. This will be added to the SourceMapGenerator
+ * in the sourcesContent field.
+ *
+ * @param aSourceFile The filename of the source file
+ * @param aSourceContent The content of the source file
+ */
+ SourceNode.prototype.setSourceContent =
+ function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
+ this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+ };
+
+ /**
+ * Walk over the tree of SourceNodes. The walking function is called for each
+ * source file content and is passed the filename and source content.
+ *
+ * @param aFn The traversal function.
+ */
+ SourceNode.prototype.walkSourceContents =
+ function SourceNode_walkSourceContents(aFn) {
+ for (var i = 0, len = this.children.length; i < len; i++) {
+ if (this.children[i] instanceof SourceNode) {
+ this.children[i].walkSourceContents(aFn);
+ }
+ }
+
+ var sources = Object.keys(this.sourceContents);
+ for (var i = 0, len = sources.length; i < len; i++) {
+ aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
+ }
+ };
+
+ /**
+ * Return the string representation of this source node. Walks over the tree
+ * and concatenates all the various snippets together to one string.
+ */
+ SourceNode.prototype.toString = function SourceNode_toString() {
+ var str = "";
+ this.walk(function (chunk) {
+ str += chunk;
+ });
+ return str;
+ };
+
+ /**
+ * Returns the string representation of this source node along with a source
+ * map.
+ */
+ SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+ var generated = {
+ code: "",
+ line: 1,
+ column: 0
+ };
+ var map = new SourceMapGenerator(aArgs);
+ var sourceMappingActive = false;
+ var lastOriginalSource = null;
+ var lastOriginalLine = null;
+ var lastOriginalColumn = null;
+ var lastOriginalName = null;
+ this.walk(function (chunk, original) {
+ generated.code += chunk;
+ if (original.source !== null
+ && original.line !== null
+ && original.column !== null) {
+ if (lastOriginalSource !== original.source
+ || lastOriginalLine !== original.line
+ || lastOriginalColumn !== original.column
+ || lastOriginalName !== original.name) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column
+ },
+ name: original.name
+ });
+ }
+ lastOriginalSource = original.source;
+ lastOriginalLine = original.line;
+ lastOriginalColumn = original.column;
+ lastOriginalName = original.name;
+ sourceMappingActive = true;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ generated: {
+ line: generated.line,
+ column: generated.column
+ }
+ });
+ lastOriginalSource = null;
+ sourceMappingActive = false;
+ }
+ chunk.split('').forEach(function (ch) {
+ if (ch === '\n') {
+ generated.line++;
+ generated.column = 0;
+ } else {
+ generated.column++;
+ }
+ });
+ });
+ this.walkSourceContents(function (sourceFile, sourceContent) {
+ map.setSourceContent(sourceFile, sourceContent);
+ });
+
+ return { code: generated.code, map: map };
+ };
+
+ exports.SourceNode = SourceNode;
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.sourceMap = {
+ SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer,
+ SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator,
+ SourceNode: require('source-map/source-node').SourceNode
+};
--- /dev/null
+// footer to wrap "source-map" module
+ return this.sourceMap;
+ }();
+})();
\ No newline at end of file
--- /dev/null
+// wraps the source-map code in a less module
+(function() {
+ less.modules["source-map"] = function() {
--- /dev/null
+../errno/cli.js
\ No newline at end of file
--- /dev/null
+../image-size/bin/image-size
\ No newline at end of file
--- /dev/null
+../mime/cli.js
\ No newline at end of file
--- /dev/null
+../mkdirp/bin/cmd.js
\ No newline at end of file
--- /dev/null
+{
+ "predef": [ ]
+ , "bitwise": false
+ , "camelcase": false
+ , "curly": false
+ , "eqeqeq": false
+ , "forin": false
+ , "immed": false
+ , "latedef": false
+ , "noarg": true
+ , "noempty": true
+ , "nonew": true
+ , "plusplus": false
+ , "quotmark": true
+ , "regexp": false
+ , "undef": true
+ , "unused": true
+ , "strict": false
+ , "trailing": true
+ , "maxlen": 120
+ , "asi": true
+ , "boss": true
+ , "debug": true
+ , "eqnull": true
+ , "esnext": true
+ , "evil": true
+ , "expr": true
+ , "funcscope": false
+ , "globalstrict": false
+ , "iterator": false
+ , "lastsemic": true
+ , "laxbreak": true
+ , "laxcomma": true
+ , "loopfunc": true
+ , "multistr": false
+ , "onecase": false
+ , "proto": false
+ , "regexdash": false
+ , "scripturl": true
+ , "smarttabs": false
+ , "shadow": false
+ , "sub": true
+ , "supernew": false
+ , "validthis": true
+ , "browser": true
+ , "couch": false
+ , "devel": false
+ , "dojo": false
+ , "mootools": false
+ , "node": true
+ , "nonstandard": true
+ , "prototypejs": false
+ , "rhino": false
+ , "worker": true
+ , "wsh": false
+ , "nomen": false
+ , "onevar": false
+ , "passfail": false
+}
\ No newline at end of file
--- /dev/null
+node_modules
\ No newline at end of file
--- /dev/null
+# node-errno
+
+Better [libuv](https://github.com/joyent/libuv)/[Node.js](http://nodejs.org) error handling & reporting. Available in npm as *errno*.
+
+* [errno exposed](#errnoexposed)
+* [Custom errors](#customerrors)
+
+<a name="errnoexposed"></a>
+## errno exposed
+
+Ever find yourself needing more details about Node.js errors? Me too, so *node-errno* contains the errno mappings direct from libuv so you can use them in your code.
+
+**By errno:**
+
+```js
+require('errno').errno[3]
+// → {
+// "errno": 3,
+// "code": "EACCES",
+// "description": "permission denied"
+// }
+```
+
+**By code:**
+
+```js
+require('errno').code.ENOTEMPTY
+// → {
+// "errno": 53,
+// "code": "ENOTEMPTY",
+// "description": "directory not empty"
+// }
+```
+
+**Make your errors more descriptive:**
+
+```js
+var errno = require('errno')
+
+function errmsg(err) {
+ var str = 'Error: '
+ // if it's a libuv error then get the description from errno
+ if (errno.errno[err.errno])
+ str += errno.errno[err.errno].description
+ else
+ str += err.message
+
+ // if it's a `fs` error then it'll have a 'path' property
+ if (err.path)
+ str += ' [' + err.path + ']'
+
+ return str
+}
+
+var fs = require('fs')
+
+fs.readFile('thisisnotarealfile.txt', function (err, data) {
+ if (err)
+ console.log(errmsg(err))
+})
+```
+
+**Use as a command line tool:**
+
+```
+~ $ errno 53
+{
+ "errno": 53,
+ "code": "ENOTEMPTY",
+ "description": "directory not empty"
+}
+~ $ errno EROFS
+{
+ "errno": 56,
+ "code": "EROFS",
+ "description": "read-only file system"
+}
+~ $ errno foo
+No such errno/code: "foo"
+```
+
+Supply no arguments for the full list. Error codes are processed case-insensitive.
+
+You will need to install with `npm install errno -g` if you want the `errno` command to be available without supplying a full path to the node_modules installation.
+
+<a name="customerrors"></a>
+## Custom errors
+
+Use `errno.custom.createError()` to create custom `Error` objects to throw around in your Node.js library. Create error heirachies so `instanceof` becomes a useful tool in tracking errors. Call-stack is correctly captured at the time you create an instance of the error object, plus a `cause` property will make available the original error object if you pass one in to the constructor.
+
+```js
+var create = require('errno').custom.createError
+var MyError = create('MyError') // inherits from Error
+var SpecificError = create('SpecificError', MyError) // inherits from MyError
+var OtherError = create('OtherError', MyError)
+
+// use them!
+if (condition) throw new SpecificError('Eeek! Something bad happened')
+
+if (err) return callback(new OtherError(err))
+```
+
+Also available is a `errno.custom.FilesystemError` with in-built access to errno properties:
+
+```js
+fs.readFile('foo', function (err, data) {
+ if (err) return callback(new errno.custom.FilesystemError(err))
+ // do something else
+})
+```
+
+The resulting error object passed through the callback will have the following properties: `code`, `errno`, `path` and `message` will contain a descriptive human-readable message.
+
+## Contributors
+
+* [bahamas10](https://github.com/bahamas10) (Dave Eddy) - Added CLI
+* [ralphtheninja](https://github.com/ralphtheninja) (Lars-Magnus Skog)
+
+## Copyright & Licence
+
+*Copyright (c) 2012-2015 [Rod Vagg](https://github.com/rvagg) ([@rvagg](https://twitter.com/rvagg))*
+
+Made available under the MIT licence:
+
+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.
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env node
+
+var request = require('request')
+ , fs = require('fs')
+
+ , uvheadloc = 'https://raw.github.com/joyent/libuv/master/include/uv.h'
+ , defreg = /^\s*XX\(\s*([\-\d]+),\s*([A-Z]+),\s*"([^"]*)"\s*\)\s*\\?$/
+
+
+request(uvheadloc, function (err, response) {
+ if (err)
+ throw err
+
+ var data, out
+
+ data = response.body
+ .split('\n')
+ .map(function (line) { return line.match(defreg) })
+ .filter(function (match) { return match })
+ .map(function (match) { return {
+ errno: parseInt(match[1], 10)
+ , code: match[2]
+ , description: match[3]
+ }})
+
+ out = 'var all = module.exports.all = ' + JSON.stringify(data, 0, 1) + '\n\n'
+
+ out += '\nmodule.exports.errno = {\n '
+ + data.map(function (e, i) {
+ return '\'' + e.errno + '\': all[' + i + ']'
+ }).join('\n , ')
+ + '\n}\n\n'
+
+ out += '\nmodule.exports.code = {\n '
+ + data.map(function (e, i) {
+ return '\'' + e.code + '\': all[' + i + ']'
+ }).join('\n , ')
+ + '\n}\n\n'
+
+ out += '\nmodule.exports.custom = require("./custom")(module.exports)\n'
+
+ fs.writeFile('errno.js', out)
+})
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env node
+
+var errno = require('./')
+ , arg = process.argv[2]
+ , data, code
+
+if (arg === undefined)
+ return console.log(JSON.stringify(errno.code, null, 2))
+
+if ((code = +arg) == arg)
+ data = errno.errno[code]
+else
+ data = errno.code[arg] || errno.code[arg.toUpperCase()]
+
+if (data)
+ console.log(JSON.stringify(data, null, 2))
+else {
+ console.error('No such errno/code: "' + arg + '"')
+ process.exit(1)
+}
\ No newline at end of file
--- /dev/null
+var prr = require('prr')
+
+function init (type, message, cause) {
+ prr(this, {
+ type : type
+ , name : type
+ // can be passed just a 'cause'
+ , cause : typeof message != 'string' ? message : cause
+ , message : !!message && typeof message != 'string' ? message.message : message
+
+ }, 'ewr')
+}
+
+// generic prototype, not intended to be actually used - helpful for `instanceof`
+function CustomError (message, cause) {
+ Error.call(this)
+ if (Error.captureStackTrace)
+ Error.captureStackTrace(this, arguments.callee)
+ init.call(this, 'CustomError', message, cause)
+}
+
+CustomError.prototype = new Error()
+
+function createError (errno, type, proto) {
+ var err = function (message, cause) {
+ init.call(this, type, message, cause)
+ //TODO: the specificity here is stupid, errno should be available everywhere
+ if (type == 'FilesystemError') {
+ this.code = this.cause.code
+ this.path = this.cause.path
+ this.errno = this.cause.errno
+ this.message =
+ (errno.errno[this.cause.errno]
+ ? errno.errno[this.cause.errno].description
+ : this.cause.message)
+ + (this.cause.path ? ' [' + this.cause.path + ']' : '')
+ }
+ Error.call(this)
+ if (Error.captureStackTrace)
+ Error.captureStackTrace(this, arguments.callee)
+ }
+ err.prototype = !!proto ? new proto() : new CustomError()
+ return err
+}
+
+module.exports = function (errno) {
+ var ce = function (type, proto) {
+ return createError(errno, type, proto)
+ }
+ return {
+ CustomError : CustomError
+ , FilesystemError : ce('FilesystemError')
+ , createError : ce
+ }
+}
--- /dev/null
+var all = module.exports.all = [
+ {
+ errno: -1,
+ code: 'UNKNOWN',
+ description: 'unknown error'
+ },
+ {
+ errno: 0,
+ code: 'OK',
+ description: 'success'
+ },
+ {
+ errno: 1,
+ code: 'EOF',
+ description: 'end of file'
+ },
+ {
+ errno: 2,
+ code: 'EADDRINFO',
+ description: 'getaddrinfo error'
+ },
+ {
+ errno: 3,
+ code: 'EACCES',
+ description: 'permission denied'
+ },
+ {
+ errno: 4,
+ code: 'EAGAIN',
+ description: 'resource temporarily unavailable'
+ },
+ {
+ errno: 5,
+ code: 'EADDRINUSE',
+ description: 'address already in use'
+ },
+ {
+ errno: 6,
+ code: 'EADDRNOTAVAIL',
+ description: 'address not available'
+ },
+ {
+ errno: 7,
+ code: 'EAFNOSUPPORT',
+ description: 'address family not supported'
+ },
+ {
+ errno: 8,
+ code: 'EALREADY',
+ description: 'connection already in progress'
+ },
+ {
+ errno: 9,
+ code: 'EBADF',
+ description: 'bad file descriptor'
+ },
+ {
+ errno: 10,
+ code: 'EBUSY',
+ description: 'resource busy or locked'
+ },
+ {
+ errno: 11,
+ code: 'ECONNABORTED',
+ description: 'software caused connection abort'
+ },
+ {
+ errno: 12,
+ code: 'ECONNREFUSED',
+ description: 'connection refused'
+ },
+ {
+ errno: 13,
+ code: 'ECONNRESET',
+ description: 'connection reset by peer'
+ },
+ {
+ errno: 14,
+ code: 'EDESTADDRREQ',
+ description: 'destination address required'
+ },
+ {
+ errno: 15,
+ code: 'EFAULT',
+ description: 'bad address in system call argument'
+ },
+ {
+ errno: 16,
+ code: 'EHOSTUNREACH',
+ description: 'host is unreachable'
+ },
+ {
+ errno: 17,
+ code: 'EINTR',
+ description: 'interrupted system call'
+ },
+ {
+ errno: 18,
+ code: 'EINVAL',
+ description: 'invalid argument'
+ },
+ {
+ errno: 19,
+ code: 'EISCONN',
+ description: 'socket is already connected'
+ },
+ {
+ errno: 20,
+ code: 'EMFILE',
+ description: 'too many open files'
+ },
+ {
+ errno: 21,
+ code: 'EMSGSIZE',
+ description: 'message too long'
+ },
+ {
+ errno: 22,
+ code: 'ENETDOWN',
+ description: 'network is down'
+ },
+ {
+ errno: 23,
+ code: 'ENETUNREACH',
+ description: 'network is unreachable'
+ },
+ {
+ errno: 24,
+ code: 'ENFILE',
+ description: 'file table overflow'
+ },
+ {
+ errno: 25,
+ code: 'ENOBUFS',
+ description: 'no buffer space available'
+ },
+ {
+ errno: 26,
+ code: 'ENOMEM',
+ description: 'not enough memory'
+ },
+ {
+ errno: 27,
+ code: 'ENOTDIR',
+ description: 'not a directory'
+ },
+ {
+ errno: 28,
+ code: 'EISDIR',
+ description: 'illegal operation on a directory'
+ },
+ {
+ errno: 29,
+ code: 'ENONET',
+ description: 'machine is not on the network'
+ },
+ {
+ errno: 31,
+ code: 'ENOTCONN',
+ description: 'socket is not connected'
+ },
+ {
+ errno: 32,
+ code: 'ENOTSOCK',
+ description: 'socket operation on non-socket'
+ },
+ {
+ errno: 33,
+ code: 'ENOTSUP',
+ description: 'operation not supported on socket'
+ },
+ {
+ errno: 34,
+ code: 'ENOENT',
+ description: 'no such file or directory'
+ },
+ {
+ errno: 35,
+ code: 'ENOSYS',
+ description: 'function not implemented'
+ },
+ {
+ errno: 36,
+ code: 'EPIPE',
+ description: 'broken pipe'
+ },
+ {
+ errno: 37,
+ code: 'EPROTO',
+ description: 'protocol error'
+ },
+ {
+ errno: 38,
+ code: 'EPROTONOSUPPORT',
+ description: 'protocol not supported'
+ },
+ {
+ errno: 39,
+ code: 'EPROTOTYPE',
+ description: 'protocol wrong type for socket'
+ },
+ {
+ errno: 40,
+ code: 'ETIMEDOUT',
+ description: 'connection timed out'
+ },
+ {
+ errno: 41,
+ code: 'ECHARSET',
+ description: 'invalid Unicode character'
+ },
+ {
+ errno: 42,
+ code: 'EAIFAMNOSUPPORT',
+ description: 'address family for hostname not supported'
+ },
+ {
+ errno: 44,
+ code: 'EAISERVICE',
+ description: 'servname not supported for ai_socktype'
+ },
+ {
+ errno: 45,
+ code: 'EAISOCKTYPE',
+ description: 'ai_socktype not supported'
+ },
+ {
+ errno: 46,
+ code: 'ESHUTDOWN',
+ description: 'cannot send after transport endpoint shutdown'
+ },
+ {
+ errno: 47,
+ code: 'EEXIST',
+ description: 'file already exists'
+ },
+ {
+ errno: 48,
+ code: 'ESRCH',
+ description: 'no such process'
+ },
+ {
+ errno: 49,
+ code: 'ENAMETOOLONG',
+ description: 'name too long'
+ },
+ {
+ errno: 50,
+ code: 'EPERM',
+ description: 'operation not permitted'
+ },
+ {
+ errno: 51,
+ code: 'ELOOP',
+ description: 'too many symbolic links encountered'
+ },
+ {
+ errno: 52,
+ code: 'EXDEV',
+ description: 'cross-device link not permitted'
+ },
+ {
+ errno: 53,
+ code: 'ENOTEMPTY',
+ description: 'directory not empty'
+ },
+ {
+ errno: 54,
+ code: 'ENOSPC',
+ description: 'no space left on device'
+ },
+ {
+ errno: 55,
+ code: 'EIO',
+ description: 'i/o error'
+ },
+ {
+ errno: 56,
+ code: 'EROFS',
+ description: 'read-only file system'
+ },
+ {
+ errno: 57,
+ code: 'ENODEV',
+ description: 'no such device'
+ },
+ {
+ errno: 58,
+ code: 'ESPIPE',
+ description: 'invalid seek'
+ },
+ {
+ errno: 59,
+ code: 'ECANCELED',
+ description: 'operation canceled'
+ }
+]
+
+module.exports.errno = {}
+module.exports.code = {}
+
+all.forEach(function (error) {
+ module.exports.errno[error.errno] = error
+ module.exports.code[error.code] = error
+})
+
+module.exports.custom = require('./custom')(module.exports)
+module.exports.create = module.exports.custom.createError
--- /dev/null
+{
+ "predef": [ ]
+ , "bitwise": false
+ , "camelcase": false
+ , "curly": false
+ , "eqeqeq": false
+ , "forin": false
+ , "immed": false
+ , "latedef": false
+ , "newcap": true
+ , "noarg": true
+ , "noempty": true
+ , "nonew": true
+ , "plusplus": false
+ , "quotmark": true
+ , "regexp": false
+ , "undef": true
+ , "unused": true
+ , "strict": false
+ , "trailing": true
+ , "maxlen": 120
+ , "asi": true
+ , "boss": true
+ , "debug": true
+ , "eqnull": true
+ , "es5": true
+ , "esnext": true
+ , "evil": true
+ , "expr": true
+ , "funcscope": false
+ , "globalstrict": false
+ , "iterator": false
+ , "lastsemic": true
+ , "laxbreak": true
+ , "laxcomma": true
+ , "loopfunc": true
+ , "multistr": false
+ , "onecase": false
+ , "proto": false
+ , "regexdash": false
+ , "scripturl": true
+ , "smarttabs": false
+ , "shadow": false
+ , "sub": true
+ , "supernew": false
+ , "validthis": true
+ , "browser": true
+ , "couch": false
+ , "devel": false
+ , "dojo": false
+ , "mootools": false
+ , "node": true
+ , "nonstandard": true
+ , "prototypejs": false
+ , "rhino": false
+ , "worker": true
+ , "wsh": false
+ , "nomen": false
+ , "onevar": true
+ , "passfail": false
+}
\ No newline at end of file
--- /dev/null
+node_modules
\ No newline at end of file
--- /dev/null
+language: node_js
+node_js:
+ - 0.8
+ - "0.10"
+branches:
+ only:
+ - master
+notifications:
+ email:
+ - rod@vagg.org
--- /dev/null
+Copyright 2013, Rod Vagg (the "Original Author")
+All rights reserved.
+
+MIT +no-false-attribs License
+
+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.
+
+Distributions of all or part of the Software intended to be used
+by the recipients as they would use the unmodified Software,
+containing modifications that substantially alter, remove, or
+disable functionality of the Software, outside of the documented
+configuration mechanisms provided by the Software, shall be
+modified such that the Original Author's bug reporting email
+addresses and urls are either replaced with the contact information
+of the parties responsible for the changes, or removed entirely.
+
+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.
+
+
+Except where noted, this license applies to any and all software
+programs and associated documentation files created by the
+Original Author, when distributed with the Software.
\ No newline at end of file
--- /dev/null
+# prr [](http://travis-ci.org/rvagg/prr)
+
+An sensible alternative to `Object.defineProperty()`. Available in npm and Ender as **prr**.
+
+## Usage
+
+Set the property `'foo'` (`obj.foo`) to have the value `'bar'` with default options (*enumerable, configurable and writable are all false*):
+
+```js
+prr(obj, 'foo', 'bar')
+```
+
+Adjust the default options:
+
+```js
+prr(obj, 'foo', 'bar', { enumerable: true, writable: true })
+```
+
+Do the same operation for multiple properties:
+
+```js
+prr(obj, { one: 'one', two: 'two' })
+// or with options:
+prr(obj, { one: 'one', two: 'two' }, { enumerable: true, writable: true })
+```
+
+But obviously, having to write out the full options object makes it nearly as bad as the original `Object.defineProperty()` so we can **simplify**.
+
+As an alternative method we can use an options string where each character represents a option: `'e'=='enumerable'`, `'c'=='configurable'` and `'w'=='writable'`:
+
+```js
+prr(obj, 'foo', 'bar', 'ew') // enumerable and writable but not configurable
+// muliple properties:
+prr(obj, { one: 'one', two: 'two' }, 'ewc') // configurable too
+```
+
+## Where can I use it?
+
+Anywhere! For pre-ES5 environments *prr* will simply fall-back to an `object[property] = value` so you can get close to what you want.
+
+*prr* is Ender-compatible so you can include it in your Ender build and `$.prr(...)` or `var prr = require('prr'); prr(...)`.
+
+## Licence
+
+prr is Copyright (c) 2013 Rod Vagg [@rvagg](https://twitter.com/rvagg) and licensed under the MIT licence. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.
\ No newline at end of file
--- /dev/null
+{
+ "name": "prr",
+ "description": "A better Object.defineProperty()",
+ "version": "0.0.0",
+ "homepage": "https://github.com/rvagg/prr",
+ "authors": [
+ "Rod Vagg <rod@vagg.org> (https://github.com/rvagg)"
+ ],
+ "keywords": [
+ "property",
+ "properties",
+ "defineProperty",
+ "ender"
+ ],
+ "main": "./prr.js",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/rvagg/prr.git"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "*"
+ },
+ "scripts": {
+ "test": "node ./test.js"
+ },
+ "license": "MIT",
+ "_id": "prr@0.0.0",
+ "dist": {
+ "shasum": "1a84b85908325501411853d0081ee3fa86e2926a",
+ "tarball": "http://registry.npmjs.org/prr/-/prr-0.0.0.tgz"
+ },
+ "_from": "prr@>=0.0.0 <0.1.0",
+ "_npmVersion": "1.2.15",
+ "_npmUser": {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ },
+ "maintainers": [
+ {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ }
+ ],
+ "directories": {},
+ "_shasum": "1a84b85908325501411853d0081ee3fa86e2926a",
+ "_resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz"
+}
--- /dev/null
+/*!
+ * prr
+ * (c) 2013 Rod Vagg <rod@vagg.org>
+ * https://github.com/rvagg/prr
+ * License: MIT
+ */
+
+(function (name, context, definition) {
+ if (typeof module != 'undefined' && module.exports)
+ module.exports = definition()
+ else
+ context[name] = definition()
+})('prr', this, function() {
+
+ var setProperty = typeof Object.defineProperty == 'function'
+ ? function (obj, key, options) {
+ Object.defineProperty(obj, key, options)
+ return obj
+ }
+ : function (obj, key, options) { // < es5
+ obj[key] = options.value
+ return obj
+ }
+
+ , makeOptions = function (value, options) {
+ var oo = typeof options == 'object'
+ , os = !oo && typeof options == 'string'
+ , op = function (p) {
+ return oo
+ ? !!options[p]
+ : os
+ ? options.indexOf(p[0]) > -1
+ : false
+ }
+
+ return {
+ enumerable : op('enumerable')
+ , configurable : op('configurable')
+ , writable : op('writable')
+ , value : value
+ }
+ }
+
+ , prr = function (obj, key, value, options) {
+ var k
+
+ options = makeOptions(value, options)
+
+ if (typeof key == 'object') {
+ for (k in key) {
+ if (Object.hasOwnProperty.call(key, k)) {
+ options.value = key[k]
+ setProperty(obj, k, options)
+ }
+ }
+ return obj
+ }
+
+ return setProperty(obj, key, options)
+ }
+
+ return prr
+})
\ No newline at end of file
--- /dev/null
+const test = require('tap').test
+ , prr = require('./')
+
+test('test prr(o, key, value) form', function (t) {
+ t.plan(2)
+
+ var o = {}
+ prr(o, 'foo', 'bar')
+ t.equal(o.foo, 'bar', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo')
+ , {
+ enumerable : false
+ , configurable : false
+ , writable : false
+ , value : 'bar'
+ }
+ , 'correct property descriptor'
+ )
+ t.end()
+})
+
+test('test prr(o, { key: value }) form', function (t) {
+ t.plan(2)
+
+ var o = {}
+ prr(o, { foo: 'bar' })
+
+ t.equal(o.foo, 'bar', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo')
+ , {
+ enumerable : false
+ , configurable : false
+ , writable : false
+ , value : 'bar'
+ }
+ , 'correct property descriptor'
+ )
+ t.end()
+})
+
+test('test multiple key:value pairs', function (t) {
+ var o = { foo: 'bar' }
+
+ prr(o, { one: 'ONE', two: 'TWO', obj: { o: 'o' }})
+
+ t.deepEqual(o, { foo: 'bar' }, 'properties are not enumerable')
+ t.equal(o.one, 'ONE', 'correctly set property')
+ t.equal(o.two, 'TWO', 'correctly set property')
+ t.deepEqual(o.obj, { o: 'o' }, 'correctly set property')
+
+ ;[ 'one', 'two', 'obj' ].forEach(function (p) {
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, p)
+ , {
+ enumerable : false
+ , configurable : false
+ , writable : false
+ , value : p == 'obj' ? { o: 'o' } : p.toUpperCase()
+ }
+ , 'correct property descriptor'
+ )
+ })
+
+ t.end()
+})
+
+test('test descriptor options', function (t) {
+ var o = {}
+
+ prr(o, 'foo', 'bar', {
+ enumerable : true
+ , configurable : false
+ })
+ t.equal(o.foo, 'bar', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo')
+ , {
+ enumerable : true
+ , configurable : false
+ , writable : false
+ , value : 'bar'
+ }
+ , 'correct property descriptor'
+ )
+
+ prr(o, 'foo2', 'bar2', {
+ enumerable : true
+ , configurable : true
+ , writable : false
+ })
+ t.equal(o.foo2, 'bar2', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo2')
+ , {
+ enumerable : true
+ , configurable : true
+ , writable : false
+ , value : 'bar2'
+ }
+ , 'correct property descriptor'
+ )
+
+ prr(o, 'foo3', 'bar3', {
+ enumerable : true
+ , configurable : true
+ , writable : true
+ })
+ t.equal(o.foo3, 'bar3', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo3')
+ , {
+ enumerable : true
+ , configurable : true
+ , writable : true
+ , value : 'bar3'
+ }
+ , 'correct property descriptor'
+ )
+
+ t.end()
+})
+
+
+test('test descriptor options, string form', function (t) {
+ var o = {}
+
+ prr(o, 'foo', 'bar', 'e')
+ t.equal(o.foo, 'bar', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo')
+ , {
+ enumerable : true
+ , configurable : false
+ , writable : false
+ , value : 'bar'
+ }
+ , 'correct property descriptor'
+ )
+
+ prr(o, 'foo2', 'bar2', 'ec')
+ t.equal(o.foo2, 'bar2', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo2')
+ , {
+ enumerable : true
+ , configurable : true
+ , writable : false
+ , value : 'bar2'
+ }
+ , 'correct property descriptor'
+ )
+
+ prr(o, 'foo3', 'bar3', 'ecw')
+ t.equal(o.foo3, 'bar3', 'correct value')
+ t.deepEqual(
+ Object.getOwnPropertyDescriptor(o, 'foo3')
+ , {
+ enumerable : true
+ , configurable : true
+ , writable : true
+ , value : 'bar3'
+ }
+ , 'correct property descriptor'
+ )
+
+ t.end()
+})
--- /dev/null
+{
+ "name": "errno",
+ "authors": [
+ "Rod Vagg @rvagg <rod@vagg.org> (https://github.com/rvagg)"
+ ],
+ "description": "libuv errno details exposed",
+ "keywords": [
+ "errors",
+ "errno",
+ "libuv"
+ ],
+ "version": "0.1.2",
+ "main": "errno.js",
+ "dependencies": {
+ "prr": "~0.0.0"
+ },
+ "bin": {
+ "errno": "./cli.js"
+ },
+ "devDependencies": {
+ "tape": "~3.5.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/rvagg/node-errno.git"
+ },
+ "license": "MIT",
+ "scripts": {
+ "test": "tape test.js"
+ },
+ "gitHead": "35e8e67ec489f2e5bf51cc1f6a1031bccdfdb588",
+ "bugs": {
+ "url": "https://github.com/rvagg/node-errno/issues"
+ },
+ "homepage": "https://github.com/rvagg/node-errno",
+ "_id": "errno@0.1.2",
+ "_shasum": "ec94dac3794a92cec9e1f08502b165933a805808",
+ "_from": "errno@>=0.1.1 <0.2.0",
+ "_npmVersion": "2.7.1",
+ "_nodeVersion": "1.5.2-nightly201503191514b82355",
+ "_npmUser": {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ },
+ "maintainers": [
+ {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ }
+ ],
+ "dist": {
+ "shasum": "ec94dac3794a92cec9e1f08502b165933a805808",
+ "tarball": "http://registry.npmjs.org/errno/-/errno-0.1.2.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/errno/-/errno-0.1.2.tgz"
+}
--- /dev/null
+#!/usr/bin/env node
+
+var test = require('tape')
+ , errno = require('./')
+
+test('sanity checks', function (t) {
+ t.ok(errno.all, 'errno.all not found')
+ t.ok(errno.errno, 'errno.errno not found')
+ t.ok(errno.code, 'errno.code not found')
+
+ t.equal(errno.all.length, 59, 'found ' + errno.all.length + ', expected 59')
+ t.equal(errno.errno['-1'], errno.all[0], 'errno -1 not first element')
+
+ t.equal(errno.code['UNKNOWN'], errno.all[0], 'code UNKNOWN not first element')
+
+ t.equal(errno.errno[1], errno.all[2], 'errno 1 not third element')
+
+ t.equal(errno.code['EOF'], errno.all[2], 'code EOF not third element')
+ t.end()
+})
+
+test('custom errors', function (t) {
+ var Cust = errno.create('FooNotBarError')
+ var cust = new Cust('foo is not bar')
+
+ t.equal(cust.name, 'FooNotBarError', 'correct custom name')
+ t.equal(cust.type, 'FooNotBarError', 'correct custom type')
+ t.equal(cust.message, 'foo is not bar', 'correct custom message')
+ t.notOk(cust.cause, 'no cause')
+ t.end()
+})
--- /dev/null
+node_modules/
--- /dev/null
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null
+# graceful-fs
+
+graceful-fs functions as a drop-in replacement for the fs module,
+making various improvements.
+
+The improvements are meant to normalize behavior across different
+platforms and environments, and to make filesystem access more
+resilient to errors.
+
+## Improvements over [fs module](http://api.nodejs.org/fs.html)
+
+graceful-fs:
+
+* Queues up `open` and `readdir` calls, and retries them once
+ something closes if there is an EMFILE error from too many file
+ descriptors.
+* fixes `lchmod` for Node versions prior to 0.6.2.
+* implements `fs.lutimes` if possible. Otherwise it becomes a noop.
+* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or
+ `lchown` if the user isn't root.
+* makes `lchmod` and `lchown` become noops, if not available.
+* retries reading a file if `read` results in EAGAIN error.
+
+On Windows, it retries renaming a file for up to one second if `EACCESS`
+or `EPERM` error occurs, likely because antivirus software has locked
+the directory.
+
+## USAGE
+
+```javascript
+// use just like fs
+var fs = require('graceful-fs')
+
+// now go and do stuff with it...
+fs.readFileSync('some-file-or-whatever')
+```
--- /dev/null
+// eeeeeevvvvviiiiiiillllll
+// more evil than monkey-patching the native builtin?
+// Not sure.
+
+var mod = require("module")
+var pre = '(function (exports, require, module, __filename, __dirname) { '
+var post = '});'
+var src = pre + process.binding('natives').fs + post
+var vm = require('vm')
+var fn = vm.runInThisContext(src)
+fn(exports, require, module, __filename, __dirname)
--- /dev/null
+// Monkey-patching the fs module.
+// It's ugly, but there is simply no other way to do this.
+var fs = module.exports = require('./fs.js')
+
+var assert = require('assert')
+
+// fix up some busted stuff, mostly on windows and old nodes
+require('./polyfills.js')
+
+var util = require('util')
+
+function noop () {}
+
+var debug = noop
+if (util.debuglog)
+ debug = util.debuglog('gfs')
+else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || ''))
+ debug = function() {
+ var m = util.format.apply(util, arguments)
+ m = 'GFS: ' + m.split(/\n/).join('\nGFS: ')
+ console.error(m)
+ }
+
+if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) {
+ process.on('exit', function() {
+ debug('fds', fds)
+ debug(queue)
+ assert.equal(queue.length, 0)
+ })
+}
+
+
+var originalOpen = fs.open
+fs.open = open
+
+function open(path, flags, mode, cb) {
+ if (typeof mode === "function") cb = mode, mode = null
+ if (typeof cb !== "function") cb = noop
+ new OpenReq(path, flags, mode, cb)
+}
+
+function OpenReq(path, flags, mode, cb) {
+ this.path = path
+ this.flags = flags
+ this.mode = mode
+ this.cb = cb
+ Req.call(this)
+}
+
+util.inherits(OpenReq, Req)
+
+OpenReq.prototype.process = function() {
+ originalOpen.call(fs, this.path, this.flags, this.mode, this.done)
+}
+
+var fds = {}
+OpenReq.prototype.done = function(er, fd) {
+ debug('open done', er, fd)
+ if (fd)
+ fds['fd' + fd] = this.path
+ Req.prototype.done.call(this, er, fd)
+}
+
+
+var originalReaddir = fs.readdir
+fs.readdir = readdir
+
+function readdir(path, cb) {
+ if (typeof cb !== "function") cb = noop
+ new ReaddirReq(path, cb)
+}
+
+function ReaddirReq(path, cb) {
+ this.path = path
+ this.cb = cb
+ Req.call(this)
+}
+
+util.inherits(ReaddirReq, Req)
+
+ReaddirReq.prototype.process = function() {
+ originalReaddir.call(fs, this.path, this.done)
+}
+
+ReaddirReq.prototype.done = function(er, files) {
+ if (files && files.sort)
+ files = files.sort()
+ Req.prototype.done.call(this, er, files)
+ onclose()
+}
+
+
+var originalClose = fs.close
+fs.close = close
+
+function close (fd, cb) {
+ debug('close', fd)
+ if (typeof cb !== "function") cb = noop
+ delete fds['fd' + fd]
+ originalClose.call(fs, fd, function(er) {
+ onclose()
+ cb(er)
+ })
+}
+
+
+var originalCloseSync = fs.closeSync
+fs.closeSync = closeSync
+
+function closeSync (fd) {
+ try {
+ return originalCloseSync(fd)
+ } finally {
+ onclose()
+ }
+}
+
+
+// Req class
+function Req () {
+ // start processing
+ this.done = this.done.bind(this)
+ this.failures = 0
+ this.process()
+}
+
+Req.prototype.done = function (er, result) {
+ var tryAgain = false
+ if (er) {
+ var code = er.code
+ var tryAgain = code === "EMFILE"
+ if (process.platform === "win32")
+ tryAgain = tryAgain || code === "OK"
+ }
+
+ if (tryAgain) {
+ this.failures ++
+ enqueue(this)
+ } else {
+ var cb = this.cb
+ cb(er, result)
+ }
+}
+
+var queue = []
+
+function enqueue(req) {
+ queue.push(req)
+ debug('enqueue %d %s', queue.length, req.constructor.name, req)
+}
+
+function onclose() {
+ var req = queue.shift()
+ if (req) {
+ debug('process', req.constructor.name, req)
+ req.process()
+ }
+}
--- /dev/null
+{
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me"
+ },
+ "name": "graceful-fs",
+ "description": "A drop-in replacement for fs, making various improvements.",
+ "version": "3.0.7",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/node-graceful-fs.git"
+ },
+ "main": "graceful-fs.js",
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "keywords": [
+ "fs",
+ "module",
+ "reading",
+ "retry",
+ "retries",
+ "queue",
+ "error",
+ "errors",
+ "handling",
+ "EMFILE",
+ "EAGAIN",
+ "EINVAL",
+ "EPERM",
+ "EACCESS"
+ ],
+ "license": "ISC",
+ "devDependencies": {
+ "mkdirp": "^0.5.0",
+ "rimraf": "^2.2.8",
+ "tap": "^0.4.13"
+ },
+ "gitHead": "4aa1ba4a0a1ae59948dc3b14decb7c4a8ab62bce",
+ "bugs": {
+ "url": "https://github.com/isaacs/node-graceful-fs/issues"
+ },
+ "homepage": "https://github.com/isaacs/node-graceful-fs#readme",
+ "_id": "graceful-fs@3.0.7",
+ "_shasum": "e935be4b3e57892d289dc3bef7be8c02779d2b54",
+ "_from": "graceful-fs@>=3.0.5 <4.0.0",
+ "_npmVersion": "2.10.0",
+ "_nodeVersion": "2.0.1",
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "isaacs@npmjs.com"
+ },
+ "dist": {
+ "shasum": "e935be4b3e57892d289dc3bef7be8c02779d2b54",
+ "tarball": "http://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.7.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ }
+ ],
+ "_resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.7.tgz"
+}
--- /dev/null
+var fs = require('./fs.js')
+var constants = require('constants')
+
+var origCwd = process.cwd
+var cwd = null
+process.cwd = function() {
+ if (!cwd)
+ cwd = origCwd.call(process)
+ return cwd
+}
+var chdir = process.chdir
+process.chdir = function(d) {
+ cwd = null
+ chdir.call(process, d)
+}
+
+// (re-)implement some things that are known busted or missing.
+
+// lchmod, broken prior to 0.6.2
+// back-port the fix here.
+if (constants.hasOwnProperty('O_SYMLINK') &&
+ process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
+ fs.lchmod = function (path, mode, callback) {
+ callback = callback || noop
+ fs.open( path
+ , constants.O_WRONLY | constants.O_SYMLINK
+ , mode
+ , function (err, fd) {
+ if (err) {
+ callback(err)
+ return
+ }
+ // prefer to return the chmod error, if one occurs,
+ // but still try to close, and report closing errors if they occur.
+ fs.fchmod(fd, mode, function (err) {
+ fs.close(fd, function(err2) {
+ callback(err || err2)
+ })
+ })
+ })
+ }
+
+ fs.lchmodSync = function (path, mode) {
+ var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
+
+ // prefer to return the chmod error, if one occurs,
+ // but still try to close, and report closing errors if they occur.
+ var err, err2
+ try {
+ var ret = fs.fchmodSync(fd, mode)
+ } catch (er) {
+ err = er
+ }
+ try {
+ fs.closeSync(fd)
+ } catch (er) {
+ err2 = er
+ }
+ if (err || err2) throw (err || err2)
+ return ret
+ }
+}
+
+
+// lutimes implementation, or no-op
+if (!fs.lutimes) {
+ if (constants.hasOwnProperty("O_SYMLINK")) {
+ fs.lutimes = function (path, at, mt, cb) {
+ fs.open(path, constants.O_SYMLINK, function (er, fd) {
+ cb = cb || noop
+ if (er) return cb(er)
+ fs.futimes(fd, at, mt, function (er) {
+ fs.close(fd, function (er2) {
+ return cb(er || er2)
+ })
+ })
+ })
+ }
+
+ fs.lutimesSync = function (path, at, mt) {
+ var fd = fs.openSync(path, constants.O_SYMLINK)
+ , err
+ , err2
+ , ret
+
+ try {
+ var ret = fs.futimesSync(fd, at, mt)
+ } catch (er) {
+ err = er
+ }
+ try {
+ fs.closeSync(fd)
+ } catch (er) {
+ err2 = er
+ }
+ if (err || err2) throw (err || err2)
+ return ret
+ }
+
+ } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
+ // maybe utimensat will be bound soonish?
+ fs.lutimes = function (path, at, mt, cb) {
+ fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
+ }
+
+ fs.lutimesSync = function (path, at, mt) {
+ return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
+ }
+
+ } else {
+ fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
+ fs.lutimesSync = function () {}
+ }
+}
+
+
+// https://github.com/isaacs/node-graceful-fs/issues/4
+// Chown should not fail on einval or eperm if non-root.
+// It should not fail on enosys ever, as this just indicates
+// that a fs doesn't support the intended operation.
+
+fs.chown = chownFix(fs.chown)
+fs.fchown = chownFix(fs.fchown)
+fs.lchown = chownFix(fs.lchown)
+
+fs.chmod = chownFix(fs.chmod)
+fs.fchmod = chownFix(fs.fchmod)
+fs.lchmod = chownFix(fs.lchmod)
+
+fs.chownSync = chownFixSync(fs.chownSync)
+fs.fchownSync = chownFixSync(fs.fchownSync)
+fs.lchownSync = chownFixSync(fs.lchownSync)
+
+fs.chmodSync = chownFix(fs.chmodSync)
+fs.fchmodSync = chownFix(fs.fchmodSync)
+fs.lchmodSync = chownFix(fs.lchmodSync)
+
+function chownFix (orig) {
+ if (!orig) return orig
+ return function (target, uid, gid, cb) {
+ return orig.call(fs, target, uid, gid, function (er, res) {
+ if (chownErOk(er)) er = null
+ cb(er, res)
+ })
+ }
+}
+
+function chownFixSync (orig) {
+ if (!orig) return orig
+ return function (target, uid, gid) {
+ try {
+ return orig.call(fs, target, uid, gid)
+ } catch (er) {
+ if (!chownErOk(er)) throw er
+ }
+ }
+}
+
+// ENOSYS means that the fs doesn't support the op. Just ignore
+// that, because it doesn't matter.
+//
+// if there's no getuid, or if getuid() is something other
+// than 0, and the error is EINVAL or EPERM, then just ignore
+// it.
+//
+// This specific case is a silent failure in cp, install, tar,
+// and most other unix tools that manage permissions.
+//
+// When running as root, or if other types of errors are
+// encountered, then it's strict.
+function chownErOk (er) {
+ if (!er)
+ return true
+
+ if (er.code === "ENOSYS")
+ return true
+
+ var nonroot = !process.getuid || process.getuid() !== 0
+ if (nonroot) {
+ if (er.code === "EINVAL" || er.code === "EPERM")
+ return true
+ }
+
+ return false
+}
+
+
+// if lchmod/lchown do not exist, then make them no-ops
+if (!fs.lchmod) {
+ fs.lchmod = function (path, mode, cb) {
+ process.nextTick(cb)
+ }
+ fs.lchmodSync = function () {}
+}
+if (!fs.lchown) {
+ fs.lchown = function (path, uid, gid, cb) {
+ process.nextTick(cb)
+ }
+ fs.lchownSync = function () {}
+}
+
+
+
+// on Windows, A/V software can lock the directory, causing this
+// to fail with an EACCES or EPERM if the directory contains newly
+// created files. Try again on failure, for up to 1 second.
+if (process.platform === "win32") {
+ var rename_ = fs.rename
+ fs.rename = function rename (from, to, cb) {
+ var start = Date.now()
+ rename_(from, to, function CB (er) {
+ if (er
+ && (er.code === "EACCES" || er.code === "EPERM")
+ && Date.now() - start < 1000) {
+ return rename_(from, to, CB)
+ }
+ if(cb) cb(er)
+ })
+ }
+}
+
+
+// if read() returns EAGAIN, then just try it again.
+var read = fs.read
+fs.read = function (fd, buffer, offset, length, position, callback_) {
+ var callback
+ if (callback_ && typeof callback_ === 'function') {
+ var eagCounter = 0
+ callback = function (er, _, __) {
+ if (er && er.code === 'EAGAIN' && eagCounter < 10) {
+ eagCounter ++
+ return read.call(fs, fd, buffer, offset, length, position, callback)
+ }
+ callback_.apply(this, arguments)
+ }
+ }
+ return read.call(fs, fd, buffer, offset, length, position, callback)
+}
+
+var readSync = fs.readSync
+fs.readSync = function (fd, buffer, offset, length, position) {
+ var eagCounter = 0
+ while (true) {
+ try {
+ return readSync.call(fs, fd, buffer, offset, length, position)
+ } catch (er) {
+ if (er.code === 'EAGAIN' && eagCounter < 10) {
+ eagCounter ++
+ continue
+ }
+ throw er
+ }
+ }
+}
+
--- /dev/null
+var test = require('tap').test
+var fs = require('../')
+
+test('open lots of stuff', function (t) {
+ // Get around EBADF from libuv by making sure that stderr is opened
+ // Otherwise Darwin will refuse to give us a FD for stderr!
+ process.stderr.write('')
+
+ // How many parallel open()'s to do
+ var n = 1024
+ var opens = 0
+ var fds = []
+ var going = true
+ var closing = false
+ var doneCalled = 0
+
+ for (var i = 0; i < n; i++) {
+ go()
+ }
+
+ function go() {
+ opens++
+ fs.open(__filename, 'r', function (er, fd) {
+ if (er) throw er
+ fds.push(fd)
+ if (going) go()
+ })
+ }
+
+ // should hit ulimit pretty fast
+ setTimeout(function () {
+ going = false
+ t.equal(opens - fds.length, n)
+ done()
+ }, 100)
+
+
+ function done () {
+ if (closing) return
+ doneCalled++
+
+ if (fds.length === 0) {
+ //console.error('done called %d times', doneCalled)
+ // First because of the timeout
+ // Then to close the fd's opened afterwards
+ // Then this time, to complete.
+ // Might take multiple passes, depending on CPU speed
+ // and ulimit, but at least 3 in every case.
+ t.ok(doneCalled >= 3)
+ return t.end()
+ }
+
+ closing = true
+ setTimeout(function () {
+ // console.error('do closing again')
+ closing = false
+ done()
+ }, 100)
+
+ // console.error('closing time')
+ var closes = fds.slice(0)
+ fds.length = 0
+ closes.forEach(function (fd) {
+ fs.close(fd, function (er) {
+ if (er) throw er
+ })
+ })
+ }
+})
--- /dev/null
+var test = require('tap').test
+var fs = require('../graceful-fs.js')
+
+test('graceful fs is monkeypatched fs', function (t) {
+ t.equal(fs, require('../fs.js'))
+ t.end()
+})
+
+test('open an existing file works', function (t) {
+ var fd = fs.openSync(__filename, 'r')
+ fs.closeSync(fd)
+ fs.open(__filename, 'r', function (er, fd) {
+ if (er) throw er
+ fs.close(fd, function (er) {
+ if (er) throw er
+ t.pass('works')
+ t.end()
+ })
+ })
+})
+
+test('open a non-existing file throws', function (t) {
+ var er
+ try {
+ var fd = fs.openSync('this file does not exist', 'r')
+ } catch (x) {
+ er = x
+ }
+ t.ok(er, 'should throw')
+ t.notOk(fd, 'should not get an fd')
+ t.equal(er.code, 'ENOENT')
+
+ fs.open('neither does this file', 'r', function (er, fd) {
+ t.ok(er, 'should throw')
+ t.notOk(fd, 'should not get an fd')
+ t.equal(er.code, 'ENOENT')
+ t.end()
+ })
+})
--- /dev/null
+var test = require("tap").test
+var fs = require("../fs.js")
+
+var readdir = fs.readdir
+fs.readdir = function(path, cb) {
+ process.nextTick(function() {
+ cb(null, ["b", "z", "a"])
+ })
+}
+
+var g = require("../")
+
+test("readdir reorder", function (t) {
+ g.readdir("whatevers", function (er, files) {
+ if (er)
+ throw er
+ t.same(files, [ "a", "b", "z" ])
+ t.end()
+ })
+})
--- /dev/null
+var fs = require('../');
+var rimraf = require('rimraf');
+var mkdirp = require('mkdirp');
+var test = require('tap').test;
+var p = require('path').resolve(__dirname, 'files');
+
+// Make sure to reserve the stderr fd
+process.stderr.write('');
+
+var num = 4097;
+var paths = new Array(num);
+
+test('make files', function (t) {
+ rimraf.sync(p);
+ mkdirp.sync(p);
+
+ for (var i = 0; i < num; ++i) {
+ paths[i] = 'files/file-' + i;
+ fs.writeFileSync(paths[i], 'content');
+ }
+
+ t.end();
+})
+
+test('read files', function (t) {
+ // now read them
+ var done = 0;
+ for (var i = 0; i < num; ++i) {
+ fs.readFile(paths[i], function(err, data) {
+ if (err)
+ throw err;
+
+ ++done;
+ if (done === num) {
+ t.pass('success');
+ t.end()
+ }
+ });
+ }
+});
+
+test('cleanup', function (t) {
+ rimraf.sync(p);
+ t.end();
+});
--- /dev/null
+.coveralls.yml
+.jshintrc
+.travis.yml
+coverage.html
+Makefile
+
+lib-cov/
+specs/
+coverage/
+*.jpg
+*.png
--- /dev/null
+##### Contributors
+* [Aditya Yadav](https://github.com/netroy)
+* [Adam Renklint](https://github.com/adamrenklint) (6Wunderkinder GmbH)
+* [Manfred Manik Nerurkar](https://github.com/Manny-MADE) (copyright owned by MADE, GmbH)
+* [Rudy Krol](https://github.com/rkrol)
+* [Linus Unnebäck](https://github.com/LinusU)
+* [Ross Johnson](https://github.com/rossj) (Mazira, LLC)
+* [Shinnosuke Watanabe](https://github.com/shinnn)
+
--- /dev/null
+The MIT License (MIT)
+
+Copyright © 2014 Aditya Yadav, http://netroy.in
+
+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.
--- /dev/null
+[](https://travis-ci.org/netroy/image-size)
+[](https://npmjs.org/package/image-size)
+[](https://www.gittip.com/netroy/)
+
+[](https://coveralls.io/r/netroy/image-size?branch=master)
+[](https://www.sidekickjs.com/r/netroy/image-size)
+[](https://codeclimate.com/github/netroy/image-size)
+[](https://gemnasium.com/netroy/image-size)
+
+#### Instalation
+
+`npm install image-size`
+
+#### Usage
+
+```javascript
+var sizeOf = require('image-size');
+var dimensions = sizeOf('images/funny-cats.png');
+console.log(dimensions.width, dimensions.height);
+```
+
+##### Async version
+```javascript
+var sizeOf = require('image-size');
+sizeOf('images/funny-cats.png', function (err, dimensions) {
+ console.log(dimensions.width, dimensions.height);
+});
+```
+
+##### Using a url
+```javascript
+var url = require('url');
+var http = require('http');
+
+var sizeOf = require('image-size');
+
+var imgUrl = 'http://my-amazing-website.com/image.jpeg';
+var options = url.parse(imgUrl);
+
+http.get(options, function (response) {
+ var chunks = [];
+ response.on('data', function (chunk) {
+ chunks.push(chunk);
+ }).on('end', function() {
+ var buffer = Buffer.concat(chunks);
+ console.log(sizeOf(buffer));
+ });
+});
+```
+You can optionally check the buffer lengths & stop downloading the image after a few kilobytes.
+**You don't need to download the entire image**
+
+#### Supported formats
+* BMP
+* GIF
+* JPEG
+* PNG
+* PSD
+* TIFF
+* WebP
+* SVG
+
+##### Upcoming
+* SWF
+
+##### Credits
+not a direct port, but an attempt to have something like
+[dabble's imagesize](https://github.com/dabble/imagesize/blob/master/lib/image_size.rb) as a node module.
+
+##### [Contributors](Contributors.md)
--- /dev/null
+#!/usr/bin/env node
+
+var fs = require('fs');
+var path = require('path');
+
+var imageSize = require('..');
+
+var files = process.argv.slice(2);
+
+if (!files.length) {
+ console.error('Usage: image-size image1 [image2] [image3] ...');
+ process.exit(-1);
+}
+
+var red = ['\x1B[31m', '\x1B[39m'];
+// var bold = ['\x1B[1m', '\x1B[22m'];
+var grey = ['\x1B[90m', '\x1B[39m'];
+var green = ['\x1B[32m', '\x1B[39m'];
+
+files.forEach(function (image) {
+ try {
+ if (fs.existsSync(path.resolve(image))) {
+ var size = imageSize(image);
+ var label = green[0] + size.width + green[1] +
+ grey[0] + 'x' + grey[1] +
+ green[0] + size.height + green[1];
+ console.info(label, '-', grey[0] + image + grey[1]);
+ } else {
+ console.error('file doesn\'t exist - ', image);
+ }
+ } catch (e) {
+ // console.error(e.stack);
+ console.error(red[0] + e.message + red[1], '-', image);
+ }
+});
\ No newline at end of file
--- /dev/null
+'use strict';
+
+var typeMap = {};
+var types = require('./types');
+
+// load all available handlers
+types.forEach(function (type) {
+ typeMap[type] = require('./types/' + type).detect;
+});
+
+module.exports = function (buffer, filepath) {
+ var type, result;
+ for (type in typeMap) {
+ result = typeMap[type](buffer, filepath);
+ if (result) {
+ return type;
+ }
+ }
+};
--- /dev/null
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+
+var libpath = process.env.TEST_COV ? '../lib-cov/' : '../lib/';
+var detector = require(libpath + 'detector');
+
+var handlers = {};
+var types = require(libpath + 'types');
+
+// load all available handlers
+types.forEach(function (type) {
+ handlers[type] = require(libpath + 'types/' + type);
+});
+
+// Maximum buffer size, with a default of 128 kilobytes.
+// TO-DO: make this adaptive based on the initial signature of the image
+var MaxBufferSize = 128*1024;
+
+function lookup (buffer, filepath) {
+ // detect the file type.. don't rely on the extension
+ var type = detector(buffer, filepath);
+
+ // find an appropriate handler for this file type
+ if (type in handlers) {
+ var size = handlers[type].calculate(buffer, filepath);
+ if (size !== false) {
+ size.type = type;
+ return size;
+ }
+ }
+
+ // throw up, if we don't understand the file
+ throw new TypeError('unsupported file type');
+}
+
+function asyncFileToBuffer (filepath, callback) {
+ // open the file in read only mode
+ fs.open(filepath, 'r', function (err, descriptor) {
+ if (err) { return callback(err); }
+ var size = fs.fstatSync(descriptor).size;
+ var bufferSize = Math.min(size, MaxBufferSize);
+ var buffer = new Buffer(bufferSize);
+ // read first buffer block from the file, asynchronously
+ fs.read(descriptor, buffer, 0, bufferSize, 0, function (err) {
+ if (err) { return callback(err); }
+ // close the file, we are done
+ fs.close(descriptor, function (err) {
+ callback(err, buffer);
+ });
+ });
+ });
+}
+
+function syncFileToBuffer (filepath) {
+ // read from the file, synchronously
+ var descriptor = fs.openSync(filepath, 'r');
+ var size = fs.fstatSync(descriptor).size;
+ var bufferSize = Math.min(size, MaxBufferSize);
+ var buffer = new Buffer(bufferSize);
+ fs.readSync(descriptor, buffer, 0, bufferSize, 0);
+ fs.closeSync(descriptor);
+ return buffer;
+}
+
+/**
+ * @params input - buffer or relative/absolute path of the image file
+ * @params callback - optional function for async detection
+ */
+module.exports = function (input, callback) {
+
+ // Handle buffer input
+ if (input instanceof Buffer) {
+ return lookup(input);
+ }
+
+ // input should be a string at this point
+ if (typeof input !== 'string') {
+ throw new TypeError('invalid invocation');
+ }
+
+ // resolve the file path
+ var filepath = path.resolve(input);
+
+ if (typeof callback === 'function') {
+ asyncFileToBuffer(filepath, function (err, buffer) {
+ if (err) { return callback(err); }
+
+ // return the dimensions
+ var dimensions;
+ try {
+ dimensions = lookup(buffer, filepath);
+ } catch (e) {
+ err = e;
+ }
+ callback(err, dimensions);
+ });
+ } else {
+ var buffer = syncFileToBuffer(filepath);
+ return lookup(buffer, filepath);
+ }
+};
--- /dev/null
+'use strict';
+
+// Abstract reading multi-byte unsigned integers
+function readUInt (buffer, bits, offset, isBigEndian) {
+ offset = offset || 0;
+ var endian = !!isBigEndian ? 'BE' : 'LE';
+ var method = buffer['readUInt' + bits + endian];
+ return method.call(buffer, offset);
+}
+
+module.exports = readUInt;
\ No newline at end of file
--- /dev/null
+'use strict';
+
+module.exports = [
+ 'bmp',
+ 'gif',
+ 'jpg',
+ 'png',
+ 'psd',
+ 'svg',
+ 'tiff',
+ 'webp'
+];
--- /dev/null
+'use strict';
+
+function isBMP (buffer) {
+ return ('BM' === buffer.toString('ascii', 0, 2));
+}
+
+function calculate (buffer) {
+ return {
+ 'width': buffer.readUInt32LE(18),
+ 'height': buffer.readUInt32LE(22)
+ };
+}
+
+module.exports = {
+ 'detect': isBMP,
+ 'calculate': calculate
+};
--- /dev/null
+'use strict';
+
+var gifRegexp = /^GIF8[7,9]a/;
+function isGIF (buffer) {
+ var signature = buffer.toString('ascii', 0, 6);
+ return (gifRegexp.test(signature));
+}
+
+function calculate(buffer) {
+ return {
+ 'width': buffer.readUInt16LE(6),
+ 'height': buffer.readUInt16LE(8)
+ };
+}
+
+module.exports = {
+ 'detect': isGIF,
+ 'calculate': calculate
+};
\ No newline at end of file
--- /dev/null
+'use strict';
+
+// NOTE: we only support baseline and progressive JPGs here
+// due to the structure of the loader class, we only get a buffer
+// with a maximum size of 4096 bytes. so if the SOF marker is outside
+// if this range we can't detect the file size correctly.
+
+// TO-DO: handle all JFIFs
+var validJFIFMarkers = {
+ 'ffdb': '0001010101', // Samsung D807 JPEG
+ 'ffe0': '4a46494600', // Standard JPEG
+ 'ffe1': '4578696600', // Camera JPEG, with EXIF data
+ 'ffe2': '4943435f50', // Canon EOS-1D JPEG
+ 'ffe3': '', // Samsung D500 JPEG
+ 'ffe8': '5350494646', // SPIFF JPEG
+ 'ffec': '4475636b79', // Photoshop JPEG
+ 'ffed': '50686f746f', // Adobe JPEG, Photoshop CMYK buffer
+ 'ffee': '41646f6265' // Adobe JPEG, Unrecognised (Lightroom??)
+};
+
+var red = ['\x1B[31m', '\x1B[39m'];
+function isJPG (buffer) { //, filepath
+ var SOIMarker = buffer.toString('hex', 0, 2);
+ var JFIFMarker = buffer.toString('hex', 2, 4);
+
+ // not a valid jpeg
+ if ('ffd8' !== SOIMarker) {
+ return false;
+ }
+
+ // TO-DO: validate the end-bytes of a jpeg file
+ // use filepath, get the last bytes, check for ffd9
+ var got = buffer.toString('hex', 6, 11);
+ var expected = JFIFMarker && validJFIFMarkers[JFIFMarker];
+ if (expected === '') {
+ console.warn(
+ red[0] +
+ 'this looks like a unrecognised jpeg\n' +
+ 'please report the issue here\n' +
+ red[1],
+ '\thttps://github.com/netroy/image-size/issues/new\n'
+ );
+ return false;
+ }
+ return (got === expected) || (JFIFMarker === 'ffdb');
+}
+
+function extractSize (buffer, i) {
+ return {
+ 'height' : buffer.readUInt16BE(i),
+ 'width' : buffer.readUInt16BE(i + 2)
+ };
+}
+
+function validateBuffer (buffer, i) {
+ // index should be within buffer limits
+ if (i > buffer.length) {
+ throw new TypeError('Corrupt JPG, exceeded buffer limits');
+ }
+ // Every JPEG block must begin with a 0xFF
+ if (buffer[i] !== 0xFF) {
+ throw new TypeError('Invalid JPG, marker table corrupted');
+ }
+}
+
+function calculate (buffer) {
+
+ // Skip 5 chars, they are for signature
+ buffer = buffer.slice(4);
+
+ var i, next;
+ while (buffer.length) {
+ // read length of the next block
+ i = buffer.readUInt16BE(0);
+
+ // ensure correct format
+ validateBuffer(buffer, i);
+
+ // 0xFFC0 is baseline(SOF)
+ // 0xFFC2 is progressive(SOF2)
+ next = buffer[i + 1];
+ if (next === 0xC0 || next === 0xC2) {
+ return extractSize(buffer, i + 5);
+ }
+
+ // move to the next block
+ buffer = buffer.slice(i + 2);
+ }
+
+ throw new TypeError('Invalid JPG, no size found');
+}
+
+module.exports = {
+ 'detect': isJPG,
+ 'calculate': calculate
+};
--- /dev/null
+'use strict';
+
+var pngSignature = 'PNG\r\n\x1a\n';
+function isPNG (buffer) {
+ if (pngSignature === buffer.toString('ascii', 1, 8)) {
+ if ('IHDR' !== buffer.toString('ascii', 12, 16)) {
+ throw new TypeError('invalid png');
+ }
+ return true;
+ }
+}
+
+function calculate (buffer) {
+ return {
+ 'width': buffer.readUInt32BE(16),
+ 'height': buffer.readUInt32BE(20)
+ };
+}
+
+module.exports = {
+ 'detect': isPNG,
+ 'calculate': calculate
+};
--- /dev/null
+'use strict';
+
+function isPSD (buffer) {
+ return ('8BPS' === buffer.toString('ascii', 0, 4));
+}
+
+function calculate (buffer) {
+ return {
+ 'width': buffer.readUInt32BE(18),
+ 'height': buffer.readUInt32BE(14)
+ };
+}
+
+module.exports = {
+ 'detect': isPSD,
+ 'calculate': calculate
+};
--- /dev/null
+'use strict';
+
+var svgReg = /<svg[^>]+[^>]*>/;
+function isSVG (buffer) {
+ return svgReg.test(buffer);
+}
+
+var extractorRegExps = {
+ 'root': /<svg [^>]+>/,
+ 'width': /(^|\s)width\s*=\s*"(.+?)"/i,
+ 'height': /(^|\s)height\s*=\s*"(.+?)"/i,
+ 'viewbox': /(^|\s)viewbox\s*=\s*"(.+?)"/i
+};
+
+function getRatio (viewbox) {
+ var ratio = 1;
+ if (viewbox && viewbox[2]) {
+ var dim = viewbox[2].split(/\s/g);
+ if (dim.length === 4) {
+ dim = dim.map(function (i) {
+ return parseInt(i, 10);
+ });
+ ratio = (dim[2] - dim[0]) / (dim[3] - dim[1]);
+ }
+ }
+ return ratio;
+}
+
+function parse (buffer) {
+ var body = buffer.toString().replace(/[\r\n\s]+/g, ' ');
+ var section = body.match(extractorRegExps.root);
+ var root = section && section[0];
+ if (root) {
+ var width = root.match(extractorRegExps.width);
+ var height = root.match(extractorRegExps.height);
+ var viewbox = root.match(extractorRegExps.viewbox);
+ var ratio = getRatio(viewbox);
+ return {
+ 'width': parseInt(width && width[2], 10) || 0,
+ 'height': parseInt(height && height[2], 10) || 0,
+ 'ratio': ratio
+ };
+ }
+}
+
+function calculate (buffer) {
+
+ var parsed = parse(buffer);
+ var width = parsed.width;
+ var height = parsed.height;
+ var ratio = parsed.ratio;
+
+ if (width && height) {
+ return { 'width': width, 'height': height };
+ } else {
+ if (width) {
+ return { 'width': width, 'height': Math.floor(width / ratio) };
+ } else if (height) {
+ return { 'width': Math.floor(height * ratio), 'height': height };
+ } else {
+ throw new TypeError('invalid svg');
+ }
+ }
+}
+
+module.exports = {
+ 'detect': isSVG,
+ 'calculate': calculate
+};
--- /dev/null
+'use strict';
+
+// based on http://www.compix.com/fileformattif.htm
+// TO-DO: support big-endian as well
+
+var fs = require('fs');
+var readUInt = require('../readUInt');
+
+function isTIFF (buffer) {
+ var hex4 = buffer.toString('hex', 0, 4);
+ return ('49492a00' === hex4 || '4d4d002a' === hex4);
+}
+
+// Read IFD (image-file-directory) into a buffer
+function readIFD (buffer, filepath, isBigEndian) {
+
+ var ifdOffset = readUInt(buffer, 32, 4, isBigEndian);
+
+ // read only till the end of the file
+ var bufferSize = 1024;
+ var fileSize = fs.statSync(filepath).size;
+ if (ifdOffset + bufferSize > fileSize) {
+ bufferSize = fileSize - ifdOffset - 10;
+ }
+
+ // populate the buffer
+ var endBuffer = new Buffer(bufferSize);
+ var descriptor = fs.openSync(filepath, 'r');
+ fs.readSync(descriptor, endBuffer, 0, bufferSize, ifdOffset);
+
+ // var ifdLength = readUInt(endBuffer, 16, 0, isBigEndian);
+ var ifdBuffer = endBuffer.slice(2); //, 2 + 12 * ifdLength);
+ return ifdBuffer;
+}
+
+// TIFF values seem to be messed up on Big-Endian, this helps
+function readValue (buffer, isBigEndian) {
+ var low = readUInt(buffer, 16, 8, isBigEndian);
+ var high = readUInt(buffer, 16, 10, isBigEndian);
+ return (high << 16) + low;
+}
+
+// move to the next tag
+function nextTag (buffer) {
+ if (buffer.length > 24) {
+ return buffer.slice(12);
+ }
+}
+
+// Extract IFD tags from TIFF metadata
+function extractTags (buffer, isBigEndian) {
+ var tags = {};
+ var code, type, length;
+
+ while (buffer && buffer.length) {
+ code = readUInt(buffer, 16, 0, isBigEndian);
+ type = readUInt(buffer, 16, 2, isBigEndian);
+ length = readUInt(buffer, 32, 4, isBigEndian);
+
+ // 0 means end of IFD
+ if (code === 0) {
+ break;
+ } else {
+ // 256 is width, 257 is height
+ // if (code === 256 || code === 257) {
+ if (length === 1 && type === 3) {
+ tags[code] = readValue(buffer, isBigEndian);
+ }
+
+ // move to the next tag
+ buffer = nextTag(buffer);
+ }
+ }
+ return tags;
+}
+
+// Test if the TIFF is Big Endian or Little Endian
+function determineEndianness (buffer) {
+ var signature = buffer.toString('ascii', 0, 2);
+ if ('II' === signature) {
+ return 'LE';
+ } else if ('MM' === signature) {
+ return 'BE';
+ }
+}
+
+function calculate (buffer, filepath) {
+
+ if (!filepath) {
+ throw new TypeError('Tiff doesn\'t support buffer');
+ }
+
+ // Determine BE/LE
+ var isBigEndian = determineEndianness(buffer) === 'BE';
+
+ // read the IFD
+ var ifdBuffer = readIFD(buffer, filepath, isBigEndian);
+
+ // extract the tags from the IFD
+ var tags = extractTags(ifdBuffer, isBigEndian);
+
+ var width = tags[256];
+ var height = tags[257];
+
+ if (!width || !height) {
+ throw new TypeError('Invalid Tiff, missing tags');
+ }
+
+ return {
+ 'width': width,
+ 'height': height
+ };
+}
+
+module.exports = {
+ 'detect': isTIFF,
+ 'calculate': calculate
+};
--- /dev/null
+'use strict';
+
+// based on https://developers.google.com/speed/webp/docs/riff_container
+
+function isWebP (buffer) {
+ var riffHeader = 'RIFF' === buffer.toString('ascii', 0, 4);
+ var webpHeader = 'WEBP' === buffer.toString('ascii', 8, 12);
+ var vp8Header = 'VP8' === buffer.toString('ascii', 12, 15);
+ return (riffHeader && webpHeader && vp8Header);
+}
+
+function calculate (buffer) {
+
+ var chunkHeader = buffer.toString('ascii', 12, 16);
+ buffer = buffer.slice(20, 30);
+
+ // Lossless webp stream signature
+ if (chunkHeader === 'VP8 ' && buffer[0] !== 0x2f) {
+ return calculateLossy(buffer);
+ }
+
+ // Lossy webp stream signature
+ var signature = buffer.toString('hex', 3, 6);
+ if (chunkHeader === 'VP8L' && signature !== '9d012a') {
+ return calculateLossless(buffer);
+ }
+
+ return false;
+}
+
+function calculateLossless (buffer) {
+ return {
+ 'width': 1 + (((buffer[2] & 0x3F) << 8) | buffer[1]),
+ 'height': 1 + (((buffer[4] & 0xF) << 10) | (buffer[3] << 2) |
+ ((buffer[2] & 0xC0) >> 6))
+ };
+}
+
+function calculateLossy (buffer) {
+ // `& 0x3fff` returns the last 14 bits
+ // TO-DO: include webp scaling in the calculations
+ return {
+ 'width': buffer.readInt16LE(6) & 0x3fff,
+ 'height': buffer.readInt16LE(8) & 0x3fff
+ };
+}
+
+module.exports = {
+ 'detect': isWebP,
+ 'calculate': calculate
+};
--- /dev/null
+{
+ "name": "image-size",
+ "version": "0.3.5",
+ "description": "get dimensions of any image file",
+ "main": "lib/index.js",
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "bin": {
+ "image-size": "./bin/image-size"
+ },
+ "scripts": {
+ "test": "make"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:netroy/image-size.git"
+ },
+ "keywords": [
+ "image",
+ "size",
+ "dimensions",
+ "resolution",
+ "width",
+ "height",
+ "png",
+ "jpeg",
+ "bmp",
+ "gif",
+ "psd",
+ "tiff",
+ "webp",
+ "svg"
+ ],
+ "author": {
+ "name": "netroy",
+ "email": "aditya@netroy.in",
+ "url": "http://netroy.in/"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/netroy/image-size/issues"
+ },
+ "devDependencies": {
+ "escomplex-js": "~0.1.4",
+ "expect.js": "~0.3.1",
+ "glob": "~4.0.6",
+ "jshint": "~2.5.6",
+ "mocha": "~1.21.5",
+ "sinon": "~1.10.3"
+ },
+ "gitHead": "ccef69913f1cac2cb88c5f6309d70a37cf097658",
+ "homepage": "https://github.com/netroy/image-size",
+ "_id": "image-size@0.3.5",
+ "_shasum": "83240eab2fb5b00b04aab8c74b0471e9cba7ad8c",
+ "_from": "image-size@>=0.3.5 <0.4.0",
+ "_npmVersion": "2.0.0",
+ "_npmUser": {
+ "name": "netroy",
+ "email": "aditya@netroy.in"
+ },
+ "maintainers": [
+ {
+ "name": "netroy",
+ "email": "aditya@netroy.in"
+ }
+ ],
+ "dist": {
+ "shasum": "83240eab2fb5b00b04aab8c74b0471e9cba7ad8c",
+ "tarball": "http://registry.npmjs.org/image-size/-/image-size-0.3.5.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/image-size/-/image-size-0.3.5.tgz"
+}
--- /dev/null
+Copyright (c) 2010 Benjamin Thomas, Robert Kieffer
+
+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.
--- /dev/null
+# mime
+
+Comprehensive MIME type mapping API based on mime-db module.
+
+## Install
+
+Install with [npm](http://github.com/isaacs/npm):
+
+ npm install mime
+
+## Contributing / Testing
+
+ npm run test
+
+## Command Line
+
+ mime [path_string]
+
+E.g.
+
+ > mime scripts/jquery.js
+ application/javascript
+
+## API - Queries
+
+### mime.lookup(path)
+Get the mime type associated with a file, if no mime type is found `application/octet-stream` is returned. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g.
+
+```js
+var mime = require('mime');
+
+mime.lookup('/path/to/file.txt'); // => 'text/plain'
+mime.lookup('file.txt'); // => 'text/plain'
+mime.lookup('.TXT'); // => 'text/plain'
+mime.lookup('htm'); // => 'text/html'
+```
+
+### mime.default_type
+Sets the mime type returned when `mime.lookup` fails to find the extension searched for. (Default is `application/octet-stream`.)
+
+### mime.extension(type)
+Get the default extension for `type`
+
+```js
+mime.extension('text/html'); // => 'html'
+mime.extension('application/octet-stream'); // => 'bin'
+```
+
+### mime.charsets.lookup()
+
+Map mime-type to charset
+
+```js
+mime.charsets.lookup('text/plain'); // => 'UTF-8'
+```
+
+(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.)
+
+## API - Defining Custom Types
+
+Custom type mappings can be added on a per-project basis via the following APIs.
+
+### mime.define()
+
+Add custom mime/extension mappings
+
+```js
+mime.define({
+ 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'],
+ 'application/x-my-type': ['x-mt', 'x-mtt'],
+ // etc ...
+});
+
+mime.lookup('x-sft'); // => 'text/x-some-format'
+```
+
+The first entry in the extensions array is returned by `mime.extension()`. E.g.
+
+```js
+mime.extension('text/x-some-format'); // => 'x-sf'
+```
+
+### mime.load(filepath)
+
+Load mappings from an Apache ".types" format file
+
+```js
+mime.load('./my_project.types');
+```
+The .types file format is simple - See the `types` dir for examples.
--- /dev/null
+var db = require('mime-db');
+
+var mapByType = {};
+Object.keys(db).forEach(function(key) {
+ var extensions = db[key].extensions;
+ if (extensions) {
+ mapByType[key] = extensions;
+ }
+});
+
+console.log(JSON.stringify(mapByType));
--- /dev/null
+/**
+ * Usage: node test.js
+ */
+
+var mime = require('../mime');
+var assert = require('assert');
+var path = require('path');
+
+//
+// Test mime lookups
+//
+
+assert.equal('text/plain', mime.lookup('text.txt')); // normal file
+assert.equal('text/plain', mime.lookup('TEXT.TXT')); // uppercase
+assert.equal('text/plain', mime.lookup('dir/text.txt')); // dir + file
+assert.equal('text/plain', mime.lookup('.text.txt')); // hidden file
+assert.equal('text/plain', mime.lookup('.txt')); // nameless
+assert.equal('text/plain', mime.lookup('txt')); // extension-only
+assert.equal('text/plain', mime.lookup('/txt')); // extension-less ()
+assert.equal('text/plain', mime.lookup('\\txt')); // Windows, extension-less
+assert.equal('application/octet-stream', mime.lookup('text.nope')); // unrecognized
+assert.equal('fallback', mime.lookup('text.fallback', 'fallback')); // alternate default
+
+//
+// Test extensions
+//
+
+assert.equal('txt', mime.extension(mime.types.text));
+assert.equal('html', mime.extension(mime.types.htm));
+assert.equal('bin', mime.extension('application/octet-stream'));
+assert.equal('bin', mime.extension('application/octet-stream '));
+assert.equal('html', mime.extension(' text/html; charset=UTF-8'));
+assert.equal('html', mime.extension('text/html; charset=UTF-8 '));
+assert.equal('html', mime.extension('text/html; charset=UTF-8'));
+assert.equal('html', mime.extension('text/html ; charset=UTF-8'));
+assert.equal('html', mime.extension('text/html;charset=UTF-8'));
+assert.equal('html', mime.extension('text/Html;charset=UTF-8'));
+assert.equal(undefined, mime.extension('unrecognized'));
+
+//
+// Test node.types lookups
+//
+
+assert.equal('application/font-woff', mime.lookup('file.woff'));
+assert.equal('application/octet-stream', mime.lookup('file.buffer'));
+assert.equal('audio/mp4', mime.lookup('file.m4a'));
+assert.equal('font/opentype', mime.lookup('file.otf'));
+
+//
+// Test charsets
+//
+
+assert.equal('UTF-8', mime.charsets.lookup('text/plain'));
+assert.equal(undefined, mime.charsets.lookup(mime.types.js));
+assert.equal('fallback', mime.charsets.lookup('application/octet-stream', 'fallback'));
+
+console.log('\nAll tests passed');
--- /dev/null
+#!/usr/bin/env node
+
+var mime = require('./mime.js');
+var file = process.argv[2];
+var type = mime.lookup(file);
+
+process.stdout.write(type + '\n');
+
--- /dev/null
+var path = require('path');
+var fs = require('fs');
+
+function Mime() {
+ // Map of extension -> mime type
+ this.types = Object.create(null);
+
+ // Map of mime type -> extension
+ this.extensions = Object.create(null);
+}
+
+/**
+ * Define mimetype -> extension mappings. Each key is a mime-type that maps
+ * to an array of extensions associated with the type. The first extension is
+ * used as the default extension for the type.
+ *
+ * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']});
+ *
+ * @param map (Object) type definitions
+ */
+Mime.prototype.define = function (map) {
+ for (var type in map) {
+ var exts = map[type];
+ for (var i = 0; i < exts.length; i++) {
+ if (process.env.DEBUG_MIME && this.types[exts]) {
+ console.warn(this._loading.replace(/.*\//, ''), 'changes "' + exts[i] + '" extension type from ' +
+ this.types[exts] + ' to ' + type);
+ }
+
+ this.types[exts[i]] = type;
+ }
+
+ // Default extension is the first one we encounter
+ if (!this.extensions[type]) {
+ this.extensions[type] = exts[0];
+ }
+ }
+};
+
+/**
+ * Load an Apache2-style ".types" file
+ *
+ * This may be called multiple times (it's expected). Where files declare
+ * overlapping types/extensions, the last file wins.
+ *
+ * @param file (String) path of file to load.
+ */
+Mime.prototype.load = function(file) {
+ this._loading = file;
+ // Read file and split into lines
+ var map = {},
+ content = fs.readFileSync(file, 'ascii'),
+ lines = content.split(/[\r\n]+/);
+
+ lines.forEach(function(line) {
+ // Clean up whitespace/comments, and split into fields
+ var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/);
+ map[fields.shift()] = fields;
+ });
+
+ this.define(map);
+
+ this._loading = null;
+};
+
+/**
+ * Lookup a mime type based on extension
+ */
+Mime.prototype.lookup = function(path, fallback) {
+ var ext = path.replace(/.*[\.\/\\]/, '').toLowerCase();
+
+ return this.types[ext] || fallback || this.default_type;
+};
+
+/**
+ * Return file extension associated with a mime type
+ */
+Mime.prototype.extension = function(mimeType) {
+ var type = mimeType.match(/^\s*([^;\s]*)(?:;|\s|$)/)[1].toLowerCase();
+ return this.extensions[type];
+};
+
+// Default instance
+var mime = new Mime();
+
+// Define built-in types
+mime.define(require('./types.json'));
+
+// Default type
+mime.default_type = mime.lookup('bin');
+
+//
+// Additional API specific to the default instance
+//
+
+mime.Mime = Mime;
+
+/**
+ * Lookup a charset based on mime type.
+ */
+mime.charsets = {
+ lookup: function(mimeType, fallback) {
+ // Assume text types are utf8
+ return (/^text\//).test(mimeType) ? 'UTF-8' : fallback;
+ }
+};
+
+module.exports = mime;
--- /dev/null
+{
+ "author": {
+ "name": "Robert Kieffer",
+ "email": "robert@broofa.com",
+ "url": "http://github.com/broofa"
+ },
+ "scripts": {
+ "prepublish": "node build/build.js > types.json",
+ "test": "node build/test.js"
+ },
+ "bin": {
+ "mime": "cli.js"
+ },
+ "contributors": [
+ {
+ "name": "Benjamin Thomas",
+ "email": "benjamin@benjaminthomas.org",
+ "url": "http://github.com/bentomas"
+ }
+ ],
+ "description": "A comprehensive library for mime-type mapping",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://raw.github.com/broofa/node-mime/master/LICENSE"
+ }
+ ],
+ "dependencies": {},
+ "devDependencies": {
+ "mime-db": "^1.2.0"
+ },
+ "keywords": [
+ "util",
+ "mime"
+ ],
+ "main": "mime.js",
+ "name": "mime",
+ "repository": {
+ "url": "https://github.com/broofa/node-mime",
+ "type": "git"
+ },
+ "version": "1.3.4",
+ "gitHead": "1628f6e0187095009dcef4805c3a49706f137974",
+ "bugs": {
+ "url": "https://github.com/broofa/node-mime/issues"
+ },
+ "homepage": "https://github.com/broofa/node-mime",
+ "_id": "mime@1.3.4",
+ "_shasum": "115f9e3b6b3daf2959983cb38f149a2d40eb5d53",
+ "_from": "mime@>=1.2.11 <2.0.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "broofa",
+ "email": "robert@broofa.com"
+ },
+ "maintainers": [
+ {
+ "name": "broofa",
+ "email": "robert@broofa.com"
+ },
+ {
+ "name": "bentomas",
+ "email": "benjamin@benjaminthomas.org"
+ }
+ ],
+ "dist": {
+ "shasum": "115f9e3b6b3daf2959983cb38f149a2d40eb5d53",
+ "tarball": "http://registry.npmjs.org/mime/-/mime-1.3.4.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz"
+}
--- /dev/null
+{"application/andrew-inset":["ez"],"application/applixware":["aw"],"application/atom+xml":["atom"],"application/atomcat+xml":["atomcat"],"application/atomsvc+xml":["atomsvc"],"application/ccxml+xml":["ccxml"],"application/cdmi-capability":["cdmia"],"application/cdmi-container":["cdmic"],"application/cdmi-domain":["cdmid"],"application/cdmi-object":["cdmio"],"application/cdmi-queue":["cdmiq"],"application/cu-seeme":["cu"],"application/dash+xml":["mdp"],"application/davmount+xml":["davmount"],"application/docbook+xml":["dbk"],"application/dssc+der":["dssc"],"application/dssc+xml":["xdssc"],"application/ecmascript":["ecma"],"application/emma+xml":["emma"],"application/epub+zip":["epub"],"application/exi":["exi"],"application/font-tdpfr":["pfr"],"application/font-woff":["woff"],"application/font-woff2":["woff2"],"application/gml+xml":["gml"],"application/gpx+xml":["gpx"],"application/gxf":["gxf"],"application/hyperstudio":["stk"],"application/inkml+xml":["ink","inkml"],"application/ipfix":["ipfix"],"application/java-archive":["jar"],"application/java-serialized-object":["ser"],"application/java-vm":["class"],"application/javascript":["js"],"application/json":["json","map"],"application/json5":["json5"],"application/jsonml+json":["jsonml"],"application/lost+xml":["lostxml"],"application/mac-binhex40":["hqx"],"application/mac-compactpro":["cpt"],"application/mads+xml":["mads"],"application/marc":["mrc"],"application/marcxml+xml":["mrcx"],"application/mathematica":["ma","nb","mb"],"application/mathml+xml":["mathml"],"application/mbox":["mbox"],"application/mediaservercontrol+xml":["mscml"],"application/metalink+xml":["metalink"],"application/metalink4+xml":["meta4"],"application/mets+xml":["mets"],"application/mods+xml":["mods"],"application/mp21":["m21","mp21"],"application/mp4":["mp4s","m4p"],"application/msword":["doc","dot"],"application/mxf":["mxf"],"application/octet-stream":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","buffer"],"application/oda":["oda"],"application/oebps-package+xml":["opf"],"application/ogg":["ogx"],"application/omdoc+xml":["omdoc"],"application/onenote":["onetoc","onetoc2","onetmp","onepkg"],"application/oxps":["oxps"],"application/patch-ops-error+xml":["xer"],"application/pdf":["pdf"],"application/pgp-encrypted":["pgp"],"application/pgp-signature":["asc","sig"],"application/pics-rules":["prf"],"application/pkcs10":["p10"],"application/pkcs7-mime":["p7m","p7c"],"application/pkcs7-signature":["p7s"],"application/pkcs8":["p8"],"application/pkix-attr-cert":["ac"],"application/pkix-cert":["cer"],"application/pkix-crl":["crl"],"application/pkix-pkipath":["pkipath"],"application/pkixcmp":["pki"],"application/pls+xml":["pls"],"application/postscript":["ai","eps","ps"],"application/prs.cww":["cww"],"application/pskc+xml":["pskcxml"],"application/rdf+xml":["rdf"],"application/reginfo+xml":["rif"],"application/relax-ng-compact-syntax":["rnc"],"application/resource-lists+xml":["rl"],"application/resource-lists-diff+xml":["rld"],"application/rls-services+xml":["rs"],"application/rpki-ghostbusters":["gbr"],"application/rpki-manifest":["mft"],"application/rpki-roa":["roa"],"application/rsd+xml":["rsd"],"application/rss+xml":["rss"],"application/rtf":["rtf"],"application/sbml+xml":["sbml"],"application/scvp-cv-request":["scq"],"application/scvp-cv-response":["scs"],"application/scvp-vp-request":["spq"],"application/scvp-vp-response":["spp"],"application/sdp":["sdp"],"application/set-payment-initiation":["setpay"],"application/set-registration-initiation":["setreg"],"application/shf+xml":["shf"],"application/smil+xml":["smi","smil"],"application/sparql-query":["rq"],"application/sparql-results+xml":["srx"],"application/srgs":["gram"],"application/srgs+xml":["grxml"],"application/sru+xml":["sru"],"application/ssdl+xml":["ssdl"],"application/ssml+xml":["ssml"],"application/tei+xml":["tei","teicorpus"],"application/thraud+xml":["tfi"],"application/timestamped-data":["tsd"],"application/vnd.3gpp.pic-bw-large":["plb"],"application/vnd.3gpp.pic-bw-small":["psb"],"application/vnd.3gpp.pic-bw-var":["pvb"],"application/vnd.3gpp2.tcap":["tcap"],"application/vnd.3m.post-it-notes":["pwn"],"application/vnd.accpac.simply.aso":["aso"],"application/vnd.accpac.simply.imp":["imp"],"application/vnd.acucobol":["acu"],"application/vnd.acucorp":["atc","acutc"],"application/vnd.adobe.air-application-installer-package+zip":["air"],"application/vnd.adobe.formscentral.fcdt":["fcdt"],"application/vnd.adobe.fxp":["fxp","fxpl"],"application/vnd.adobe.xdp+xml":["xdp"],"application/vnd.adobe.xfdf":["xfdf"],"application/vnd.ahead.space":["ahead"],"application/vnd.airzip.filesecure.azf":["azf"],"application/vnd.airzip.filesecure.azs":["azs"],"application/vnd.amazon.ebook":["azw"],"application/vnd.americandynamics.acc":["acc"],"application/vnd.amiga.ami":["ami"],"application/vnd.android.package-archive":["apk"],"application/vnd.anser-web-certificate-issue-initiation":["cii"],"application/vnd.anser-web-funds-transfer-initiation":["fti"],"application/vnd.antix.game-component":["atx"],"application/vnd.apple.installer+xml":["mpkg"],"application/vnd.apple.mpegurl":["m3u8"],"application/vnd.aristanetworks.swi":["swi"],"application/vnd.astraea-software.iota":["iota"],"application/vnd.audiograph":["aep"],"application/vnd.blueice.multipass":["mpm"],"application/vnd.bmi":["bmi"],"application/vnd.businessobjects":["rep"],"application/vnd.chemdraw+xml":["cdxml"],"application/vnd.chipnuts.karaoke-mmd":["mmd"],"application/vnd.cinderella":["cdy"],"application/vnd.claymore":["cla"],"application/vnd.cloanto.rp9":["rp9"],"application/vnd.clonk.c4group":["c4g","c4d","c4f","c4p","c4u"],"application/vnd.cluetrust.cartomobile-config":["c11amc"],"application/vnd.cluetrust.cartomobile-config-pkg":["c11amz"],"application/vnd.commonspace":["csp"],"application/vnd.contact.cmsg":["cdbcmsg"],"application/vnd.cosmocaller":["cmc"],"application/vnd.crick.clicker":["clkx"],"application/vnd.crick.clicker.keyboard":["clkk"],"application/vnd.crick.clicker.palette":["clkp"],"application/vnd.crick.clicker.template":["clkt"],"application/vnd.crick.clicker.wordbank":["clkw"],"application/vnd.criticaltools.wbs+xml":["wbs"],"application/vnd.ctc-posml":["pml"],"application/vnd.cups-ppd":["ppd"],"application/vnd.curl.car":["car"],"application/vnd.curl.pcurl":["pcurl"],"application/vnd.dart":["dart"],"application/vnd.data-vision.rdz":["rdz"],"application/vnd.dece.data":["uvf","uvvf","uvd","uvvd"],"application/vnd.dece.ttml+xml":["uvt","uvvt"],"application/vnd.dece.unspecified":["uvx","uvvx"],"application/vnd.dece.zip":["uvz","uvvz"],"application/vnd.denovo.fcselayout-link":["fe_launch"],"application/vnd.dna":["dna"],"application/vnd.dolby.mlp":["mlp"],"application/vnd.dpgraph":["dpg"],"application/vnd.dreamfactory":["dfac"],"application/vnd.ds-keypoint":["kpxx"],"application/vnd.dvb.ait":["ait"],"application/vnd.dvb.service":["svc"],"application/vnd.dynageo":["geo"],"application/vnd.ecowin.chart":["mag"],"application/vnd.enliven":["nml"],"application/vnd.epson.esf":["esf"],"application/vnd.epson.msf":["msf"],"application/vnd.epson.quickanime":["qam"],"application/vnd.epson.salt":["slt"],"application/vnd.epson.ssf":["ssf"],"application/vnd.eszigno3+xml":["es3","et3"],"application/vnd.ezpix-album":["ez2"],"application/vnd.ezpix-package":["ez3"],"application/vnd.fdf":["fdf"],"application/vnd.fdsn.mseed":["mseed"],"application/vnd.fdsn.seed":["seed","dataless"],"application/vnd.flographit":["gph"],"application/vnd.fluxtime.clip":["ftc"],"application/vnd.framemaker":["fm","frame","maker","book"],"application/vnd.frogans.fnc":["fnc"],"application/vnd.frogans.ltf":["ltf"],"application/vnd.fsc.weblaunch":["fsc"],"application/vnd.fujitsu.oasys":["oas"],"application/vnd.fujitsu.oasys2":["oa2"],"application/vnd.fujitsu.oasys3":["oa3"],"application/vnd.fujitsu.oasysgp":["fg5"],"application/vnd.fujitsu.oasysprs":["bh2"],"application/vnd.fujixerox.ddd":["ddd"],"application/vnd.fujixerox.docuworks":["xdw"],"application/vnd.fujixerox.docuworks.binder":["xbd"],"application/vnd.fuzzysheet":["fzs"],"application/vnd.genomatix.tuxedo":["txd"],"application/vnd.geogebra.file":["ggb"],"application/vnd.geogebra.tool":["ggt"],"application/vnd.geometry-explorer":["gex","gre"],"application/vnd.geonext":["gxt"],"application/vnd.geoplan":["g2w"],"application/vnd.geospace":["g3w"],"application/vnd.gmx":["gmx"],"application/vnd.google-earth.kml+xml":["kml"],"application/vnd.google-earth.kmz":["kmz"],"application/vnd.grafeq":["gqf","gqs"],"application/vnd.groove-account":["gac"],"application/vnd.groove-help":["ghf"],"application/vnd.groove-identity-message":["gim"],"application/vnd.groove-injector":["grv"],"application/vnd.groove-tool-message":["gtm"],"application/vnd.groove-tool-template":["tpl"],"application/vnd.groove-vcard":["vcg"],"application/vnd.hal+xml":["hal"],"application/vnd.handheld-entertainment+xml":["zmm"],"application/vnd.hbci":["hbci"],"application/vnd.hhe.lesson-player":["les"],"application/vnd.hp-hpgl":["hpgl"],"application/vnd.hp-hpid":["hpid"],"application/vnd.hp-hps":["hps"],"application/vnd.hp-jlyt":["jlt"],"application/vnd.hp-pcl":["pcl"],"application/vnd.hp-pclxl":["pclxl"],"application/vnd.ibm.minipay":["mpy"],"application/vnd.ibm.modcap":["afp","listafp","list3820"],"application/vnd.ibm.rights-management":["irm"],"application/vnd.ibm.secure-container":["sc"],"application/vnd.iccprofile":["icc","icm"],"application/vnd.igloader":["igl"],"application/vnd.immervision-ivp":["ivp"],"application/vnd.immervision-ivu":["ivu"],"application/vnd.insors.igm":["igm"],"application/vnd.intercon.formnet":["xpw","xpx"],"application/vnd.intergeo":["i2g"],"application/vnd.intu.qbo":["qbo"],"application/vnd.intu.qfx":["qfx"],"application/vnd.ipunplugged.rcprofile":["rcprofile"],"application/vnd.irepository.package+xml":["irp"],"application/vnd.is-xpr":["xpr"],"application/vnd.isac.fcs":["fcs"],"application/vnd.jam":["jam"],"application/vnd.jcp.javame.midlet-rms":["rms"],"application/vnd.jisp":["jisp"],"application/vnd.joost.joda-archive":["joda"],"application/vnd.kahootz":["ktz","ktr"],"application/vnd.kde.karbon":["karbon"],"application/vnd.kde.kchart":["chrt"],"application/vnd.kde.kformula":["kfo"],"application/vnd.kde.kivio":["flw"],"application/vnd.kde.kontour":["kon"],"application/vnd.kde.kpresenter":["kpr","kpt"],"application/vnd.kde.kspread":["ksp"],"application/vnd.kde.kword":["kwd","kwt"],"application/vnd.kenameaapp":["htke"],"application/vnd.kidspiration":["kia"],"application/vnd.kinar":["kne","knp"],"application/vnd.koan":["skp","skd","skt","skm"],"application/vnd.kodak-descriptor":["sse"],"application/vnd.las.las+xml":["lasxml"],"application/vnd.llamagraphics.life-balance.desktop":["lbd"],"application/vnd.llamagraphics.life-balance.exchange+xml":["lbe"],"application/vnd.lotus-1-2-3":["123"],"application/vnd.lotus-approach":["apr"],"application/vnd.lotus-freelance":["pre"],"application/vnd.lotus-notes":["nsf"],"application/vnd.lotus-organizer":["org"],"application/vnd.lotus-screencam":["scm"],"application/vnd.lotus-wordpro":["lwp"],"application/vnd.macports.portpkg":["portpkg"],"application/vnd.mcd":["mcd"],"application/vnd.medcalcdata":["mc1"],"application/vnd.mediastation.cdkey":["cdkey"],"application/vnd.mfer":["mwf"],"application/vnd.mfmp":["mfm"],"application/vnd.micrografx.flo":["flo"],"application/vnd.micrografx.igx":["igx"],"application/vnd.mif":["mif"],"application/vnd.mobius.daf":["daf"],"application/vnd.mobius.dis":["dis"],"application/vnd.mobius.mbk":["mbk"],"application/vnd.mobius.mqy":["mqy"],"application/vnd.mobius.msl":["msl"],"application/vnd.mobius.plc":["plc"],"application/vnd.mobius.txf":["txf"],"application/vnd.mophun.application":["mpn"],"application/vnd.mophun.certificate":["mpc"],"application/vnd.mozilla.xul+xml":["xul"],"application/vnd.ms-artgalry":["cil"],"application/vnd.ms-cab-compressed":["cab"],"application/vnd.ms-excel":["xls","xlm","xla","xlc","xlt","xlw"],"application/vnd.ms-excel.addin.macroenabled.12":["xlam"],"application/vnd.ms-excel.sheet.binary.macroenabled.12":["xlsb"],"application/vnd.ms-excel.sheet.macroenabled.12":["xlsm"],"application/vnd.ms-excel.template.macroenabled.12":["xltm"],"application/vnd.ms-fontobject":["eot"],"application/vnd.ms-htmlhelp":["chm"],"application/vnd.ms-ims":["ims"],"application/vnd.ms-lrm":["lrm"],"application/vnd.ms-officetheme":["thmx"],"application/vnd.ms-pki.seccat":["cat"],"application/vnd.ms-pki.stl":["stl"],"application/vnd.ms-powerpoint":["ppt","pps","pot"],"application/vnd.ms-powerpoint.addin.macroenabled.12":["ppam"],"application/vnd.ms-powerpoint.presentation.macroenabled.12":["pptm"],"application/vnd.ms-powerpoint.slide.macroenabled.12":["sldm"],"application/vnd.ms-powerpoint.slideshow.macroenabled.12":["ppsm"],"application/vnd.ms-powerpoint.template.macroenabled.12":["potm"],"application/vnd.ms-project":["mpp","mpt"],"application/vnd.ms-word.document.macroenabled.12":["docm"],"application/vnd.ms-word.template.macroenabled.12":["dotm"],"application/vnd.ms-works":["wps","wks","wcm","wdb"],"application/vnd.ms-wpl":["wpl"],"application/vnd.ms-xpsdocument":["xps"],"application/vnd.mseq":["mseq"],"application/vnd.musician":["mus"],"application/vnd.muvee.style":["msty"],"application/vnd.mynfc":["taglet"],"application/vnd.neurolanguage.nlu":["nlu"],"application/vnd.nitf":["ntf","nitf"],"application/vnd.noblenet-directory":["nnd"],"application/vnd.noblenet-sealer":["nns"],"application/vnd.noblenet-web":["nnw"],"application/vnd.nokia.n-gage.data":["ngdat"],"application/vnd.nokia.radio-preset":["rpst"],"application/vnd.nokia.radio-presets":["rpss"],"application/vnd.novadigm.edm":["edm"],"application/vnd.novadigm.edx":["edx"],"application/vnd.novadigm.ext":["ext"],"application/vnd.oasis.opendocument.chart":["odc"],"application/vnd.oasis.opendocument.chart-template":["otc"],"application/vnd.oasis.opendocument.database":["odb"],"application/vnd.oasis.opendocument.formula":["odf"],"application/vnd.oasis.opendocument.formula-template":["odft"],"application/vnd.oasis.opendocument.graphics":["odg"],"application/vnd.oasis.opendocument.graphics-template":["otg"],"application/vnd.oasis.opendocument.image":["odi"],"application/vnd.oasis.opendocument.image-template":["oti"],"application/vnd.oasis.opendocument.presentation":["odp"],"application/vnd.oasis.opendocument.presentation-template":["otp"],"application/vnd.oasis.opendocument.spreadsheet":["ods"],"application/vnd.oasis.opendocument.spreadsheet-template":["ots"],"application/vnd.oasis.opendocument.text":["odt"],"application/vnd.oasis.opendocument.text-master":["odm"],"application/vnd.oasis.opendocument.text-template":["ott"],"application/vnd.oasis.opendocument.text-web":["oth"],"application/vnd.olpc-sugar":["xo"],"application/vnd.oma.dd2+xml":["dd2"],"application/vnd.openofficeorg.extension":["oxt"],"application/vnd.openxmlformats-officedocument.presentationml.presentation":["pptx"],"application/vnd.openxmlformats-officedocument.presentationml.slide":["sldx"],"application/vnd.openxmlformats-officedocument.presentationml.slideshow":["ppsx"],"application/vnd.openxmlformats-officedocument.presentationml.template":["potx"],"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":["xlsx"],"application/vnd.openxmlformats-officedocument.spreadsheetml.template":["xltx"],"application/vnd.openxmlformats-officedocument.wordprocessingml.document":["docx"],"application/vnd.openxmlformats-officedocument.wordprocessingml.template":["dotx"],"application/vnd.osgeo.mapguide.package":["mgp"],"application/vnd.osgi.dp":["dp"],"application/vnd.osgi.subsystem":["esa"],"application/vnd.palm":["pdb","pqa","oprc"],"application/vnd.pawaafile":["paw"],"application/vnd.pg.format":["str"],"application/vnd.pg.osasli":["ei6"],"application/vnd.picsel":["efif"],"application/vnd.pmi.widget":["wg"],"application/vnd.pocketlearn":["plf"],"application/vnd.powerbuilder6":["pbd"],"application/vnd.previewsystems.box":["box"],"application/vnd.proteus.magazine":["mgz"],"application/vnd.publishare-delta-tree":["qps"],"application/vnd.pvi.ptid1":["ptid"],"application/vnd.quark.quarkxpress":["qxd","qxt","qwd","qwt","qxl","qxb"],"application/vnd.realvnc.bed":["bed"],"application/vnd.recordare.musicxml":["mxl"],"application/vnd.recordare.musicxml+xml":["musicxml"],"application/vnd.rig.cryptonote":["cryptonote"],"application/vnd.rim.cod":["cod"],"application/vnd.rn-realmedia":["rm"],"application/vnd.rn-realmedia-vbr":["rmvb"],"application/vnd.route66.link66+xml":["link66"],"application/vnd.sailingtracker.track":["st"],"application/vnd.seemail":["see"],"application/vnd.sema":["sema"],"application/vnd.semd":["semd"],"application/vnd.semf":["semf"],"application/vnd.shana.informed.formdata":["ifm"],"application/vnd.shana.informed.formtemplate":["itp"],"application/vnd.shana.informed.interchange":["iif"],"application/vnd.shana.informed.package":["ipk"],"application/vnd.simtech-mindmapper":["twd","twds"],"application/vnd.smaf":["mmf"],"application/vnd.smart.teacher":["teacher"],"application/vnd.solent.sdkm+xml":["sdkm","sdkd"],"application/vnd.spotfire.dxp":["dxp"],"application/vnd.spotfire.sfs":["sfs"],"application/vnd.stardivision.calc":["sdc"],"application/vnd.stardivision.draw":["sda"],"application/vnd.stardivision.impress":["sdd"],"application/vnd.stardivision.math":["smf"],"application/vnd.stardivision.writer":["sdw","vor"],"application/vnd.stardivision.writer-global":["sgl"],"application/vnd.stepmania.package":["smzip"],"application/vnd.stepmania.stepchart":["sm"],"application/vnd.sun.xml.calc":["sxc"],"application/vnd.sun.xml.calc.template":["stc"],"application/vnd.sun.xml.draw":["sxd"],"application/vnd.sun.xml.draw.template":["std"],"application/vnd.sun.xml.impress":["sxi"],"application/vnd.sun.xml.impress.template":["sti"],"application/vnd.sun.xml.math":["sxm"],"application/vnd.sun.xml.writer":["sxw"],"application/vnd.sun.xml.writer.global":["sxg"],"application/vnd.sun.xml.writer.template":["stw"],"application/vnd.sus-calendar":["sus","susp"],"application/vnd.svd":["svd"],"application/vnd.symbian.install":["sis","sisx"],"application/vnd.syncml+xml":["xsm"],"application/vnd.syncml.dm+wbxml":["bdm"],"application/vnd.syncml.dm+xml":["xdm"],"application/vnd.tao.intent-module-archive":["tao"],"application/vnd.tcpdump.pcap":["pcap","cap","dmp"],"application/vnd.tmobile-livetv":["tmo"],"application/vnd.trid.tpt":["tpt"],"application/vnd.triscape.mxs":["mxs"],"application/vnd.trueapp":["tra"],"application/vnd.ufdl":["ufd","ufdl"],"application/vnd.uiq.theme":["utz"],"application/vnd.umajin":["umj"],"application/vnd.unity":["unityweb"],"application/vnd.uoml+xml":["uoml"],"application/vnd.vcx":["vcx"],"application/vnd.visio":["vsd","vst","vss","vsw"],"application/vnd.visionary":["vis"],"application/vnd.vsf":["vsf"],"application/vnd.wap.wbxml":["wbxml"],"application/vnd.wap.wmlc":["wmlc"],"application/vnd.wap.wmlscriptc":["wmlsc"],"application/vnd.webturbo":["wtb"],"application/vnd.wolfram.player":["nbp"],"application/vnd.wordperfect":["wpd"],"application/vnd.wqd":["wqd"],"application/vnd.wt.stf":["stf"],"application/vnd.xara":["xar"],"application/vnd.xfdl":["xfdl"],"application/vnd.yamaha.hv-dic":["hvd"],"application/vnd.yamaha.hv-script":["hvs"],"application/vnd.yamaha.hv-voice":["hvp"],"application/vnd.yamaha.openscoreformat":["osf"],"application/vnd.yamaha.openscoreformat.osfpvg+xml":["osfpvg"],"application/vnd.yamaha.smaf-audio":["saf"],"application/vnd.yamaha.smaf-phrase":["spf"],"application/vnd.yellowriver-custom-menu":["cmp"],"application/vnd.zul":["zir","zirz"],"application/vnd.zzazz.deck+xml":["zaz"],"application/voicexml+xml":["vxml"],"application/widget":["wgt"],"application/winhlp":["hlp"],"application/wsdl+xml":["wsdl"],"application/wspolicy+xml":["wspolicy"],"application/x-7z-compressed":["7z"],"application/x-abiword":["abw"],"application/x-ace-compressed":["ace"],"application/x-apple-diskimage":["dmg"],"application/x-authorware-bin":["aab","x32","u32","vox"],"application/x-authorware-map":["aam"],"application/x-authorware-seg":["aas"],"application/x-bcpio":["bcpio"],"application/x-bittorrent":["torrent"],"application/x-blorb":["blb","blorb"],"application/x-bzip":["bz"],"application/x-bzip2":["bz2","boz"],"application/x-cbr":["cbr","cba","cbt","cbz","cb7"],"application/x-cdlink":["vcd"],"application/x-cfs-compressed":["cfs"],"application/x-chat":["chat"],"application/x-chess-pgn":["pgn"],"application/x-chrome-extension":["crx"],"application/x-conference":["nsc"],"application/x-cpio":["cpio"],"application/x-csh":["csh"],"application/x-debian-package":["deb","udeb"],"application/x-dgc-compressed":["dgc"],"application/x-director":["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"],"application/x-doom":["wad"],"application/x-dtbncx+xml":["ncx"],"application/x-dtbook+xml":["dtb"],"application/x-dtbresource+xml":["res"],"application/x-dvi":["dvi"],"application/x-envoy":["evy"],"application/x-eva":["eva"],"application/x-font-bdf":["bdf"],"application/x-font-ghostscript":["gsf"],"application/x-font-linux-psf":["psf"],"application/x-font-otf":["otf"],"application/x-font-pcf":["pcf"],"application/x-font-snf":["snf"],"application/x-font-ttf":["ttf","ttc"],"application/x-font-type1":["pfa","pfb","pfm","afm"],"application/x-freearc":["arc"],"application/x-futuresplash":["spl"],"application/x-gca-compressed":["gca"],"application/x-glulx":["ulx"],"application/x-gnumeric":["gnumeric"],"application/x-gramps-xml":["gramps"],"application/x-gtar":["gtar"],"application/x-hdf":["hdf"],"application/x-install-instructions":["install"],"application/x-iso9660-image":["iso"],"application/x-java-jnlp-file":["jnlp"],"application/x-latex":["latex"],"application/x-lua-bytecode":["luac"],"application/x-lzh-compressed":["lzh","lha"],"application/x-mie":["mie"],"application/x-mobipocket-ebook":["prc","mobi"],"application/x-ms-application":["application"],"application/x-ms-shortcut":["lnk"],"application/x-ms-wmd":["wmd"],"application/x-ms-wmz":["wmz"],"application/x-ms-xbap":["xbap"],"application/x-msaccess":["mdb"],"application/x-msbinder":["obd"],"application/x-mscardfile":["crd"],"application/x-msclip":["clp"],"application/x-msdownload":["exe","dll","com","bat","msi"],"application/x-msmediaview":["mvb","m13","m14"],"application/x-msmetafile":["wmf","wmz","emf","emz"],"application/x-msmoney":["mny"],"application/x-mspublisher":["pub"],"application/x-msschedule":["scd"],"application/x-msterminal":["trm"],"application/x-mswrite":["wri"],"application/x-netcdf":["nc","cdf"],"application/x-nzb":["nzb"],"application/x-pkcs12":["p12","pfx"],"application/x-pkcs7-certificates":["p7b","spc"],"application/x-pkcs7-certreqresp":["p7r"],"application/x-rar-compressed":["rar"],"application/x-research-info-systems":["ris"],"application/x-sh":["sh"],"application/x-shar":["shar"],"application/x-shockwave-flash":["swf"],"application/x-silverlight-app":["xap"],"application/x-sql":["sql"],"application/x-stuffit":["sit"],"application/x-stuffitx":["sitx"],"application/x-subrip":["srt"],"application/x-sv4cpio":["sv4cpio"],"application/x-sv4crc":["sv4crc"],"application/x-t3vm-image":["t3"],"application/x-tads":["gam"],"application/x-tar":["tar"],"application/x-tcl":["tcl"],"application/x-tex":["tex"],"application/x-tex-tfm":["tfm"],"application/x-texinfo":["texinfo","texi"],"application/x-tgif":["obj"],"application/x-ustar":["ustar"],"application/x-wais-source":["src"],"application/x-web-app-manifest+json":["webapp"],"application/x-x509-ca-cert":["der","crt"],"application/x-xfig":["fig"],"application/x-xliff+xml":["xlf"],"application/x-xpinstall":["xpi"],"application/x-xz":["xz"],"application/x-zmachine":["z1","z2","z3","z4","z5","z6","z7","z8"],"application/xaml+xml":["xaml"],"application/xcap-diff+xml":["xdf"],"application/xenc+xml":["xenc"],"application/xhtml+xml":["xhtml","xht"],"application/xml":["xml","xsl","xsd"],"application/xml-dtd":["dtd"],"application/xop+xml":["xop"],"application/xproc+xml":["xpl"],"application/xslt+xml":["xslt"],"application/xspf+xml":["xspf"],"application/xv+xml":["mxml","xhvml","xvml","xvm"],"application/yang":["yang"],"application/yin+xml":["yin"],"application/zip":["zip"],"audio/adpcm":["adp"],"audio/basic":["au","snd"],"audio/midi":["mid","midi","kar","rmi"],"audio/mp4":["mp4a","m4a"],"audio/mpeg":["mpga","mp2","mp2a","mp3","m2a","m3a"],"audio/ogg":["oga","ogg","spx"],"audio/s3m":["s3m"],"audio/silk":["sil"],"audio/vnd.dece.audio":["uva","uvva"],"audio/vnd.digital-winds":["eol"],"audio/vnd.dra":["dra"],"audio/vnd.dts":["dts"],"audio/vnd.dts.hd":["dtshd"],"audio/vnd.lucent.voice":["lvp"],"audio/vnd.ms-playready.media.pya":["pya"],"audio/vnd.nuera.ecelp4800":["ecelp4800"],"audio/vnd.nuera.ecelp7470":["ecelp7470"],"audio/vnd.nuera.ecelp9600":["ecelp9600"],"audio/vnd.rip":["rip"],"audio/webm":["weba"],"audio/x-aac":["aac"],"audio/x-aiff":["aif","aiff","aifc"],"audio/x-caf":["caf"],"audio/x-flac":["flac"],"audio/x-matroska":["mka"],"audio/x-mpegurl":["m3u"],"audio/x-ms-wax":["wax"],"audio/x-ms-wma":["wma"],"audio/x-pn-realaudio":["ram","ra"],"audio/x-pn-realaudio-plugin":["rmp"],"audio/x-wav":["wav"],"audio/xm":["xm"],"chemical/x-cdx":["cdx"],"chemical/x-cif":["cif"],"chemical/x-cmdf":["cmdf"],"chemical/x-cml":["cml"],"chemical/x-csml":["csml"],"chemical/x-xyz":["xyz"],"font/opentype":["otf"],"image/bmp":["bmp"],"image/cgm":["cgm"],"image/g3fax":["g3"],"image/gif":["gif"],"image/ief":["ief"],"image/jpeg":["jpeg","jpg","jpe"],"image/ktx":["ktx"],"image/png":["png"],"image/prs.btif":["btif"],"image/sgi":["sgi"],"image/svg+xml":["svg","svgz"],"image/tiff":["tiff","tif"],"image/vnd.adobe.photoshop":["psd"],"image/vnd.dece.graphic":["uvi","uvvi","uvg","uvvg"],"image/vnd.djvu":["djvu","djv"],"image/vnd.dvb.subtitle":["sub"],"image/vnd.dwg":["dwg"],"image/vnd.dxf":["dxf"],"image/vnd.fastbidsheet":["fbs"],"image/vnd.fpx":["fpx"],"image/vnd.fst":["fst"],"image/vnd.fujixerox.edmics-mmr":["mmr"],"image/vnd.fujixerox.edmics-rlc":["rlc"],"image/vnd.ms-modi":["mdi"],"image/vnd.ms-photo":["wdp"],"image/vnd.net-fpx":["npx"],"image/vnd.wap.wbmp":["wbmp"],"image/vnd.xiff":["xif"],"image/webp":["webp"],"image/x-3ds":["3ds"],"image/x-cmu-raster":["ras"],"image/x-cmx":["cmx"],"image/x-freehand":["fh","fhc","fh4","fh5","fh7"],"image/x-icon":["ico"],"image/x-mrsid-image":["sid"],"image/x-pcx":["pcx"],"image/x-pict":["pic","pct"],"image/x-portable-anymap":["pnm"],"image/x-portable-bitmap":["pbm"],"image/x-portable-graymap":["pgm"],"image/x-portable-pixmap":["ppm"],"image/x-rgb":["rgb"],"image/x-tga":["tga"],"image/x-xbitmap":["xbm"],"image/x-xpixmap":["xpm"],"image/x-xwindowdump":["xwd"],"message/rfc822":["eml","mime"],"model/iges":["igs","iges"],"model/mesh":["msh","mesh","silo"],"model/vnd.collada+xml":["dae"],"model/vnd.dwf":["dwf"],"model/vnd.gdl":["gdl"],"model/vnd.gtw":["gtw"],"model/vnd.mts":["mts"],"model/vnd.vtu":["vtu"],"model/vrml":["wrl","vrml"],"model/x3d+binary":["x3db","x3dbz"],"model/x3d+vrml":["x3dv","x3dvz"],"model/x3d+xml":["x3d","x3dz"],"text/cache-manifest":["appcache","manifest"],"text/calendar":["ics","ifb"],"text/coffeescript":["coffee"],"text/css":["css"],"text/csv":["csv"],"text/hjson":["hjson"],"text/html":["html","htm"],"text/jade":["jade"],"text/jsx":["jsx"],"text/less":["less"],"text/n3":["n3"],"text/plain":["txt","text","conf","def","list","log","in","ini"],"text/prs.lines.tag":["dsc"],"text/richtext":["rtx"],"text/sgml":["sgml","sgm"],"text/stylus":["stylus","styl"],"text/tab-separated-values":["tsv"],"text/troff":["t","tr","roff","man","me","ms"],"text/turtle":["ttl"],"text/uri-list":["uri","uris","urls"],"text/vcard":["vcard"],"text/vnd.curl":["curl"],"text/vnd.curl.dcurl":["dcurl"],"text/vnd.curl.mcurl":["mcurl"],"text/vnd.curl.scurl":["scurl"],"text/vnd.dvb.subtitle":["sub"],"text/vnd.fly":["fly"],"text/vnd.fmi.flexstor":["flx"],"text/vnd.graphviz":["gv"],"text/vnd.in3d.3dml":["3dml"],"text/vnd.in3d.spot":["spot"],"text/vnd.sun.j2me.app-descriptor":["jad"],"text/vnd.wap.wml":["wml"],"text/vnd.wap.wmlscript":["wmls"],"text/vtt":["vtt"],"text/x-asm":["s","asm"],"text/x-c":["c","cc","cxx","cpp","h","hh","dic"],"text/x-component":["htc"],"text/x-fortran":["f","for","f77","f90"],"text/x-handlebars-template":["hbs"],"text/x-java-source":["java"],"text/x-lua":["lua"],"text/x-markdown":["markdown","md","mkd"],"text/x-nfo":["nfo"],"text/x-opml":["opml"],"text/x-pascal":["p","pas"],"text/x-sass":["sass"],"text/x-scss":["scss"],"text/x-setext":["etx"],"text/x-sfv":["sfv"],"text/x-uuencode":["uu"],"text/x-vcalendar":["vcs"],"text/x-vcard":["vcf"],"text/yaml":["yaml","yml"],"video/3gpp":["3gp"],"video/3gpp2":["3g2"],"video/h261":["h261"],"video/h263":["h263"],"video/h264":["h264"],"video/jpeg":["jpgv"],"video/jpm":["jpm","jpgm"],"video/mj2":["mj2","mjp2"],"video/mp2t":["ts"],"video/mp4":["mp4","mp4v","mpg4"],"video/mpeg":["mpeg","mpg","mpe","m1v","m2v"],"video/ogg":["ogv"],"video/quicktime":["qt","mov"],"video/vnd.dece.hd":["uvh","uvvh"],"video/vnd.dece.mobile":["uvm","uvvm"],"video/vnd.dece.pd":["uvp","uvvp"],"video/vnd.dece.sd":["uvs","uvvs"],"video/vnd.dece.video":["uvv","uvvv"],"video/vnd.dvb.file":["dvb"],"video/vnd.fvt":["fvt"],"video/vnd.mpegurl":["mxu","m4u"],"video/vnd.ms-playready.media.pyv":["pyv"],"video/vnd.uvvu.mp4":["uvu","uvvu"],"video/vnd.vivo":["viv"],"video/webm":["webm"],"video/x-f4v":["f4v"],"video/x-fli":["fli"],"video/x-flv":["flv"],"video/x-m4v":["m4v"],"video/x-matroska":["mkv","mk3d","mks"],"video/x-mng":["mng"],"video/x-ms-asf":["asf","asx"],"video/x-ms-vob":["vob"],"video/x-ms-wm":["wm"],"video/x-ms-wmv":["wmv"],"video/x-ms-wmx":["wmx"],"video/x-ms-wvx":["wvx"],"video/x-msvideo":["avi"],"video/x-sgi-movie":["movie"],"video/x-smv":["smv"],"x-conference/x-cooltalk":["ice"]}
--- /dev/null
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
+ - "0.12"
+ - "iojs"
+before_install:
+ - npm install -g npm@~1.4.6
--- /dev/null
+Copyright 2010 James Halliday (mail@substack.net)
+
+This project is free software released under the MIT/X11 license:
+
+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.
--- /dev/null
+#!/usr/bin/env node
+
+var mkdirp = require('../');
+var minimist = require('minimist');
+var fs = require('fs');
+
+var argv = minimist(process.argv.slice(2), {
+ alias: { m: 'mode', h: 'help' },
+ string: [ 'mode' ]
+});
+if (argv.help) {
+ fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout);
+ return;
+}
+
+var paths = argv._.slice();
+var mode = argv.mode ? parseInt(argv.mode, 8) : undefined;
+
+(function next () {
+ if (paths.length === 0) return;
+ var p = paths.shift();
+
+ if (mode === undefined) mkdirp(p, cb)
+ else mkdirp(p, mode, cb)
+
+ function cb (err) {
+ if (err) {
+ console.error(err.message);
+ process.exit(1);
+ }
+ else next();
+ }
+})();
--- /dev/null
+usage: mkdirp [DIR1,DIR2..] {OPTIONS}
+
+ Create each supplied directory including any necessary parent directories that
+ don't yet exist.
+
+ If the directory already exists, do nothing.
+
+OPTIONS are:
+
+ -m, --mode If a directory needs to be created, set the mode as an octal
+ permission string.
+
--- /dev/null
+var mkdirp = require('mkdirp');
+
+mkdirp('/tmp/foo/bar/baz', function (err) {
+ if (err) console.error(err)
+ else console.log('pow!')
+});
--- /dev/null
+var path = require('path');
+var fs = require('fs');
+var _0777 = parseInt('0777', 8);
+
+module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
+
+function mkdirP (p, opts, f, made) {
+ if (typeof opts === 'function') {
+ f = opts;
+ opts = {};
+ }
+ else if (!opts || typeof opts !== 'object') {
+ opts = { mode: opts };
+ }
+
+ var mode = opts.mode;
+ var xfs = opts.fs || fs;
+
+ if (mode === undefined) {
+ mode = _0777 & (~process.umask());
+ }
+ if (!made) made = null;
+
+ var cb = f || function () {};
+ p = path.resolve(p);
+
+ xfs.mkdir(p, mode, function (er) {
+ if (!er) {
+ made = made || p;
+ return cb(null, made);
+ }
+ switch (er.code) {
+ case 'ENOENT':
+ mkdirP(path.dirname(p), opts, function (er, made) {
+ if (er) cb(er, made);
+ else mkdirP(p, opts, cb, made);
+ });
+ break;
+
+ // In the case of any other error, just see if there's a dir
+ // there already. If so, then hooray! If not, then something
+ // is borked.
+ default:
+ xfs.stat(p, function (er2, stat) {
+ // if the stat fails, then that's super weird.
+ // let the original error be the failure reason.
+ if (er2 || !stat.isDirectory()) cb(er, made)
+ else cb(null, made);
+ });
+ break;
+ }
+ });
+}
+
+mkdirP.sync = function sync (p, opts, made) {
+ if (!opts || typeof opts !== 'object') {
+ opts = { mode: opts };
+ }
+
+ var mode = opts.mode;
+ var xfs = opts.fs || fs;
+
+ if (mode === undefined) {
+ mode = _0777 & (~process.umask());
+ }
+ if (!made) made = null;
+
+ p = path.resolve(p);
+
+ try {
+ xfs.mkdirSync(p, mode);
+ made = made || p;
+ }
+ catch (err0) {
+ switch (err0.code) {
+ case 'ENOENT' :
+ made = sync(path.dirname(p), opts, made);
+ sync(p, opts, made);
+ break;
+
+ // In the case of any other error, just see if there's a dir
+ // there already. If so, then hooray! If not, then something
+ // is borked.
+ default:
+ var stat;
+ try {
+ stat = xfs.statSync(p);
+ }
+ catch (err1) {
+ throw err0;
+ }
+ if (!stat.isDirectory()) throw err0;
+ break;
+ }
+ }
+
+ return made;
+};
--- /dev/null
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
--- /dev/null
+This software is released under the MIT license:
+
+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.
--- /dev/null
+var argv = require('../')(process.argv.slice(2));
+console.dir(argv);
--- /dev/null
+module.exports = function (args, opts) {
+ if (!opts) opts = {};
+
+ var flags = { bools : {}, strings : {} };
+
+ [].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
+ flags.bools[key] = true;
+ });
+
+ [].concat(opts.string).filter(Boolean).forEach(function (key) {
+ flags.strings[key] = true;
+ });
+
+ var aliases = {};
+ Object.keys(opts.alias || {}).forEach(function (key) {
+ aliases[key] = [].concat(opts.alias[key]);
+ aliases[key].forEach(function (x) {
+ aliases[x] = [key].concat(aliases[key].filter(function (y) {
+ return x !== y;
+ }));
+ });
+ });
+
+ var defaults = opts['default'] || {};
+
+ var argv = { _ : [] };
+ Object.keys(flags.bools).forEach(function (key) {
+ setArg(key, defaults[key] === undefined ? false : defaults[key]);
+ });
+
+ var notFlags = [];
+
+ if (args.indexOf('--') !== -1) {
+ notFlags = args.slice(args.indexOf('--')+1);
+ args = args.slice(0, args.indexOf('--'));
+ }
+
+ function setArg (key, val) {
+ var value = !flags.strings[key] && isNumber(val)
+ ? Number(val) : val
+ ;
+ setKey(argv, key.split('.'), value);
+
+ (aliases[key] || []).forEach(function (x) {
+ setKey(argv, x.split('.'), value);
+ });
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+
+ if (/^--.+=/.test(arg)) {
+ // Using [\s\S] instead of . because js doesn't support the
+ // 'dotall' regex modifier. See:
+ // http://stackoverflow.com/a/1068308/13216
+ var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
+ setArg(m[1], m[2]);
+ }
+ else if (/^--no-.+/.test(arg)) {
+ var key = arg.match(/^--no-(.+)/)[1];
+ setArg(key, false);
+ }
+ else if (/^--.+/.test(arg)) {
+ var key = arg.match(/^--(.+)/)[1];
+ var next = args[i + 1];
+ if (next !== undefined && !/^-/.test(next)
+ && !flags.bools[key]
+ && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
+ setArg(key, next);
+ i++;
+ }
+ else if (/^(true|false)$/.test(next)) {
+ setArg(key, next === 'true');
+ i++;
+ }
+ else {
+ setArg(key, flags.strings[key] ? '' : true);
+ }
+ }
+ else if (/^-[^-]+/.test(arg)) {
+ var letters = arg.slice(1,-1).split('');
+
+ var broken = false;
+ for (var j = 0; j < letters.length; j++) {
+ var next = arg.slice(j+2);
+
+ if (next === '-') {
+ setArg(letters[j], next)
+ continue;
+ }
+
+ if (/[A-Za-z]/.test(letters[j])
+ && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
+ setArg(letters[j], next);
+ broken = true;
+ break;
+ }
+
+ if (letters[j+1] && letters[j+1].match(/\W/)) {
+ setArg(letters[j], arg.slice(j+2));
+ broken = true;
+ break;
+ }
+ else {
+ setArg(letters[j], flags.strings[letters[j]] ? '' : true);
+ }
+ }
+
+ var key = arg.slice(-1)[0];
+ if (!broken && key !== '-') {
+ if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
+ && !flags.bools[key]
+ && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
+ setArg(key, args[i+1]);
+ i++;
+ }
+ else if (args[i+1] && /true|false/.test(args[i+1])) {
+ setArg(key, args[i+1] === 'true');
+ i++;
+ }
+ else {
+ setArg(key, flags.strings[key] ? '' : true);
+ }
+ }
+ }
+ else {
+ argv._.push(
+ flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
+ );
+ }
+ }
+
+ Object.keys(defaults).forEach(function (key) {
+ if (!hasKey(argv, key.split('.'))) {
+ setKey(argv, key.split('.'), defaults[key]);
+
+ (aliases[key] || []).forEach(function (x) {
+ setKey(argv, x.split('.'), defaults[key]);
+ });
+ }
+ });
+
+ notFlags.forEach(function(key) {
+ argv._.push(key);
+ });
+
+ return argv;
+};
+
+function hasKey (obj, keys) {
+ var o = obj;
+ keys.slice(0,-1).forEach(function (key) {
+ o = (o[key] || {});
+ });
+
+ var key = keys[keys.length - 1];
+ return key in o;
+}
+
+function setKey (obj, keys, value) {
+ var o = obj;
+ keys.slice(0,-1).forEach(function (key) {
+ if (o[key] === undefined) o[key] = {};
+ o = o[key];
+ });
+
+ var key = keys[keys.length - 1];
+ if (o[key] === undefined || typeof o[key] === 'boolean') {
+ o[key] = value;
+ }
+ else if (Array.isArray(o[key])) {
+ o[key].push(value);
+ }
+ else {
+ o[key] = [ o[key], value ];
+ }
+}
+
+function isNumber (x) {
+ if (typeof x === 'number') return true;
+ if (/^0x[0-9a-f]+$/i.test(x)) return true;
+ return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
+}
+
+function longest (xs) {
+ return Math.max.apply(null, xs.map(function (x) { return x.length }));
+}
--- /dev/null
+{
+ "name": "minimist",
+ "version": "0.0.8",
+ "description": "parse argument options",
+ "main": "index.js",
+ "devDependencies": {
+ "tape": "~1.0.4",
+ "tap": "~0.4.0"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "testling": {
+ "files": "test/*.js",
+ "browsers": [
+ "ie/6..latest",
+ "ff/5",
+ "firefox/latest",
+ "chrome/10",
+ "chrome/latest",
+ "safari/5.1",
+ "safari/latest",
+ "opera/12"
+ ]
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/substack/minimist.git"
+ },
+ "homepage": "https://github.com/substack/minimist",
+ "keywords": [
+ "argv",
+ "getopt",
+ "parser",
+ "optimist"
+ ],
+ "author": {
+ "name": "James Halliday",
+ "email": "mail@substack.net",
+ "url": "http://substack.net"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/substack/minimist/issues"
+ },
+ "_id": "minimist@0.0.8",
+ "dist": {
+ "shasum": "857fcabfc3397d2625b8228262e86aa7a011b05d",
+ "tarball": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+ },
+ "_from": "minimist@0.0.8",
+ "_npmVersion": "1.4.3",
+ "_npmUser": {
+ "name": "substack",
+ "email": "mail@substack.net"
+ },
+ "maintainers": [
+ {
+ "name": "substack",
+ "email": "mail@substack.net"
+ }
+ ],
+ "directories": {},
+ "_shasum": "857fcabfc3397d2625b8228262e86aa7a011b05d",
+ "_resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+}
--- /dev/null
+# minimist
+
+parse argument options
+
+This module is the guts of optimist's argument parser without all the
+fanciful decoration.
+
+[](http://ci.testling.com/substack/minimist)
+
+[](http://travis-ci.org/substack/minimist)
+
+# example
+
+``` js
+var argv = require('minimist')(process.argv.slice(2));
+console.dir(argv);
+```
+
+```
+$ node example/parse.js -a beep -b boop
+{ _: [], a: 'beep', b: 'boop' }
+```
+
+```
+$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
+{ _: [ 'foo', 'bar', 'baz' ],
+ x: 3,
+ y: 4,
+ n: 5,
+ a: true,
+ b: true,
+ c: true,
+ beep: 'boop' }
+```
+
+# methods
+
+``` js
+var parseArgs = require('minimist')
+```
+
+## var argv = parseArgs(args, opts={})
+
+Return an argument object `argv` populated with the array arguments from `args`.
+
+`argv._` contains all the arguments that didn't have an option associated with
+them.
+
+Numeric-looking arguments will be returned as numbers unless `opts.string` or
+`opts.boolean` is set for that argument name.
+
+Any arguments after `'--'` will not be parsed and will end up in `argv._`.
+
+options can be:
+
+* `opts.string` - a string or array of strings argument names to always treat as
+strings
+* `opts.boolean` - a string or array of strings to always treat as booleans
+* `opts.alias` - an object mapping string names to strings or arrays of string
+argument names to use as aliases
+* `opts.default` - an object mapping string argument names to default values
+
+# install
+
+With [npm](https://npmjs.org) do:
+
+```
+npm install minimist
+```
+
+# license
+
+MIT
--- /dev/null
+var parse = require('../');
+var test = require('tape');
+
+test('-', function (t) {
+ t.plan(5);
+ t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] });
+ t.deepEqual(parse([ '-' ]), { _: [ '-' ] });
+ t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] });
+ t.deepEqual(
+ parse([ '-b', '-' ], { boolean: 'b' }),
+ { b: true, _: [ '-' ] }
+ );
+ t.deepEqual(
+ parse([ '-s', '-' ], { string: 's' }),
+ { s: '-', _: [] }
+ );
+});
+
+test('-a -- b', function (t) {
+ t.plan(3);
+ t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] });
+ t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
+ t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
+});
--- /dev/null
+var test = require('tape');
+var parse = require('../');
+
+test('boolean default true', function (t) {
+ var argv = parse([], {
+ boolean: 'sometrue',
+ default: { sometrue: true }
+ });
+ t.equal(argv.sometrue, true);
+ t.end();
+});
+
+test('boolean default false', function (t) {
+ var argv = parse([], {
+ boolean: 'somefalse',
+ default: { somefalse: false }
+ });
+ t.equal(argv.somefalse, false);
+ t.end();
+});
--- /dev/null
+var parse = require('../');
+var test = require('tape');
+
+test('dotted alias', function (t) {
+ var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
+ t.equal(argv.a.b, 22);
+ t.equal(argv.aa.bb, 22);
+ t.end();
+});
+
+test('dotted default', function (t) {
+ var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
+ t.equal(argv.a.b, 11);
+ t.equal(argv.aa.bb, 11);
+ t.end();
+});
--- /dev/null
+var test = require('tape');
+var parse = require('../');
+
+test('long opts', function (t) {
+ t.deepEqual(
+ parse([ '--bool' ]),
+ { bool : true, _ : [] },
+ 'long boolean'
+ );
+ t.deepEqual(
+ parse([ '--pow', 'xixxle' ]),
+ { pow : 'xixxle', _ : [] },
+ 'long capture sp'
+ );
+ t.deepEqual(
+ parse([ '--pow=xixxle' ]),
+ { pow : 'xixxle', _ : [] },
+ 'long capture eq'
+ );
+ t.deepEqual(
+ parse([ '--host', 'localhost', '--port', '555' ]),
+ { host : 'localhost', port : 555, _ : [] },
+ 'long captures sp'
+ );
+ t.deepEqual(
+ parse([ '--host=localhost', '--port=555' ]),
+ { host : 'localhost', port : 555, _ : [] },
+ 'long captures eq'
+ );
+ t.end();
+});
--- /dev/null
+var parse = require('../');
+var test = require('tape');
+
+test('parse args', function (t) {
+ t.deepEqual(
+ parse([ '--no-moo' ]),
+ { moo : false, _ : [] },
+ 'no'
+ );
+ t.deepEqual(
+ parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]),
+ { v : ['a','b','c'], _ : [] },
+ 'multi'
+ );
+ t.end();
+});
+
+test('comprehensive', function (t) {
+ t.deepEqual(
+ parse([
+ '--name=meowmers', 'bare', '-cats', 'woo',
+ '-h', 'awesome', '--multi=quux',
+ '--key', 'value',
+ '-b', '--bool', '--no-meep', '--multi=baz',
+ '--', '--not-a-flag', 'eek'
+ ]),
+ {
+ c : true,
+ a : true,
+ t : true,
+ s : 'woo',
+ h : 'awesome',
+ b : true,
+ bool : true,
+ key : 'value',
+ multi : [ 'quux', 'baz' ],
+ meep : false,
+ name : 'meowmers',
+ _ : [ 'bare', '--not-a-flag', 'eek' ]
+ }
+ );
+ t.end();
+});
+
+test('nums', function (t) {
+ var argv = parse([
+ '-x', '1234',
+ '-y', '5.67',
+ '-z', '1e7',
+ '-w', '10f',
+ '--hex', '0xdeadbeef',
+ '789'
+ ]);
+ t.deepEqual(argv, {
+ x : 1234,
+ y : 5.67,
+ z : 1e7,
+ w : '10f',
+ hex : 0xdeadbeef,
+ _ : [ 789 ]
+ });
+ t.deepEqual(typeof argv.x, 'number');
+ t.deepEqual(typeof argv.y, 'number');
+ t.deepEqual(typeof argv.z, 'number');
+ t.deepEqual(typeof argv.w, 'string');
+ t.deepEqual(typeof argv.hex, 'number');
+ t.deepEqual(typeof argv._[0], 'number');
+ t.end();
+});
+
+test('flag boolean', function (t) {
+ var argv = parse([ '-t', 'moo' ], { boolean: 't' });
+ t.deepEqual(argv, { t : true, _ : [ 'moo' ] });
+ t.deepEqual(typeof argv.t, 'boolean');
+ t.end();
+});
+
+test('flag boolean value', function (t) {
+ var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], {
+ boolean: [ 't', 'verbose' ],
+ default: { verbose: true }
+ });
+
+ t.deepEqual(argv, {
+ verbose: false,
+ t: true,
+ _: ['moo']
+ });
+
+ t.deepEqual(typeof argv.verbose, 'boolean');
+ t.deepEqual(typeof argv.t, 'boolean');
+ t.end();
+});
+
+test('flag boolean default false', function (t) {
+ var argv = parse(['moo'], {
+ boolean: ['t', 'verbose'],
+ default: { verbose: false, t: false }
+ });
+
+ t.deepEqual(argv, {
+ verbose: false,
+ t: false,
+ _: ['moo']
+ });
+
+ t.deepEqual(typeof argv.verbose, 'boolean');
+ t.deepEqual(typeof argv.t, 'boolean');
+ t.end();
+
+});
+
+test('boolean groups', function (t) {
+ var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], {
+ boolean: ['x','y','z']
+ });
+
+ t.deepEqual(argv, {
+ x : true,
+ y : false,
+ z : true,
+ _ : [ 'one', 'two', 'three' ]
+ });
+
+ t.deepEqual(typeof argv.x, 'boolean');
+ t.deepEqual(typeof argv.y, 'boolean');
+ t.deepEqual(typeof argv.z, 'boolean');
+ t.end();
+});
+
+test('newlines in params' , function (t) {
+ var args = parse([ '-s', "X\nX" ])
+ t.deepEqual(args, { _ : [], s : "X\nX" });
+
+ // reproduce in bash:
+ // VALUE="new
+ // line"
+ // node program.js --s="$VALUE"
+ args = parse([ "--s=X\nX" ])
+ t.deepEqual(args, { _ : [], s : "X\nX" });
+ t.end();
+});
+
+test('strings' , function (t) {
+ var s = parse([ '-s', '0001234' ], { string: 's' }).s;
+ t.equal(s, '0001234');
+ t.equal(typeof s, 'string');
+
+ var x = parse([ '-x', '56' ], { string: 'x' }).x;
+ t.equal(x, '56');
+ t.equal(typeof x, 'string');
+ t.end();
+});
+
+test('stringArgs', function (t) {
+ var s = parse([ ' ', ' ' ], { string: '_' })._;
+ t.same(s.length, 2);
+ t.same(typeof s[0], 'string');
+ t.same(s[0], ' ');
+ t.same(typeof s[1], 'string');
+ t.same(s[1], ' ');
+ t.end();
+});
+
+test('empty strings', function(t) {
+ var s = parse([ '-s' ], { string: 's' }).s;
+ t.equal(s, '');
+ t.equal(typeof s, 'string');
+
+ var str = parse([ '--str' ], { string: 'str' }).str;
+ t.equal(str, '');
+ t.equal(typeof str, 'string');
+
+ var letters = parse([ '-art' ], {
+ string: [ 'a', 't' ]
+ });
+
+ t.equal(letters.a, '');
+ t.equal(letters.r, true);
+ t.equal(letters.t, '');
+
+ t.end();
+});
+
+
+test('slashBreak', function (t) {
+ t.same(
+ parse([ '-I/foo/bar/baz' ]),
+ { I : '/foo/bar/baz', _ : [] }
+ );
+ t.same(
+ parse([ '-xyz/foo/bar/baz' ]),
+ { x : true, y : true, z : '/foo/bar/baz', _ : [] }
+ );
+ t.end();
+});
+
+test('alias', function (t) {
+ var argv = parse([ '-f', '11', '--zoom', '55' ], {
+ alias: { z: 'zoom' }
+ });
+ t.equal(argv.zoom, 55);
+ t.equal(argv.z, argv.zoom);
+ t.equal(argv.f, 11);
+ t.end();
+});
+
+test('multiAlias', function (t) {
+ var argv = parse([ '-f', '11', '--zoom', '55' ], {
+ alias: { z: [ 'zm', 'zoom' ] }
+ });
+ t.equal(argv.zoom, 55);
+ t.equal(argv.z, argv.zoom);
+ t.equal(argv.z, argv.zm);
+ t.equal(argv.f, 11);
+ t.end();
+});
+
+test('nested dotted objects', function (t) {
+ var argv = parse([
+ '--foo.bar', '3', '--foo.baz', '4',
+ '--foo.quux.quibble', '5', '--foo.quux.o_O',
+ '--beep.boop'
+ ]);
+
+ t.same(argv.foo, {
+ bar : 3,
+ baz : 4,
+ quux : {
+ quibble : 5,
+ o_O : true
+ }
+ });
+ t.same(argv.beep, { boop : true });
+ t.end();
+});
+
+test('boolean and alias with chainable api', function (t) {
+ var aliased = [ '-h', 'derp' ];
+ var regular = [ '--herp', 'derp' ];
+ var opts = {
+ herp: { alias: 'h', boolean: true }
+ };
+ var aliasedArgv = parse(aliased, {
+ boolean: 'herp',
+ alias: { h: 'herp' }
+ });
+ var propertyArgv = parse(regular, {
+ boolean: 'herp',
+ alias: { h: 'herp' }
+ });
+ var expected = {
+ herp: true,
+ h: true,
+ '_': [ 'derp' ]
+ };
+
+ t.same(aliasedArgv, expected);
+ t.same(propertyArgv, expected);
+ t.end();
+});
+
+test('boolean and alias with options hash', function (t) {
+ var aliased = [ '-h', 'derp' ];
+ var regular = [ '--herp', 'derp' ];
+ var opts = {
+ alias: { 'h': 'herp' },
+ boolean: 'herp'
+ };
+ var aliasedArgv = parse(aliased, opts);
+ var propertyArgv = parse(regular, opts);
+ var expected = {
+ herp: true,
+ h: true,
+ '_': [ 'derp' ]
+ };
+ t.same(aliasedArgv, expected);
+ t.same(propertyArgv, expected);
+ t.end();
+});
+
+test('boolean and alias using explicit true', function (t) {
+ var aliased = [ '-h', 'true' ];
+ var regular = [ '--herp', 'true' ];
+ var opts = {
+ alias: { h: 'herp' },
+ boolean: 'h'
+ };
+ var aliasedArgv = parse(aliased, opts);
+ var propertyArgv = parse(regular, opts);
+ var expected = {
+ herp: true,
+ h: true,
+ '_': [ ]
+ };
+
+ t.same(aliasedArgv, expected);
+ t.same(propertyArgv, expected);
+ t.end();
+});
+
+// regression, see https://github.com/substack/node-optimist/issues/71
+test('boolean and --x=true', function(t) {
+ var parsed = parse(['--boool', '--other=true'], {
+ boolean: 'boool'
+ });
+
+ t.same(parsed.boool, true);
+ t.same(parsed.other, 'true');
+
+ parsed = parse(['--boool', '--other=false'], {
+ boolean: 'boool'
+ });
+
+ t.same(parsed.boool, true);
+ t.same(parsed.other, 'false');
+ t.end();
+});
--- /dev/null
+var parse = require('../');
+var test = require('tape');
+
+test('parse with modifier functions' , function (t) {
+ t.plan(1);
+
+ var argv = parse([ '-b', '123' ], { boolean: 'b' });
+ t.deepEqual(argv, { b: true, _: ['123'] });
+});
--- /dev/null
+var parse = require('../');
+var test = require('tape');
+
+test('numeric short args', function (t) {
+ t.plan(2);
+ t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] });
+ t.deepEqual(
+ parse([ '-123', '456' ]),
+ { 1: true, 2: true, 3: 456, _: [] }
+ );
+});
+
+test('short', function (t) {
+ t.deepEqual(
+ parse([ '-b' ]),
+ { b : true, _ : [] },
+ 'short boolean'
+ );
+ t.deepEqual(
+ parse([ 'foo', 'bar', 'baz' ]),
+ { _ : [ 'foo', 'bar', 'baz' ] },
+ 'bare'
+ );
+ t.deepEqual(
+ parse([ '-cats' ]),
+ { c : true, a : true, t : true, s : true, _ : [] },
+ 'group'
+ );
+ t.deepEqual(
+ parse([ '-cats', 'meow' ]),
+ { c : true, a : true, t : true, s : 'meow', _ : [] },
+ 'short group next'
+ );
+ t.deepEqual(
+ parse([ '-h', 'localhost' ]),
+ { h : 'localhost', _ : [] },
+ 'short capture'
+ );
+ t.deepEqual(
+ parse([ '-h', 'localhost', '-p', '555' ]),
+ { h : 'localhost', p : 555, _ : [] },
+ 'short captures'
+ );
+ t.end();
+});
+
+test('mixed short bool and capture', function (t) {
+ t.same(
+ parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
+ {
+ f : true, p : 555, h : 'localhost',
+ _ : [ 'script.js' ]
+ }
+ );
+ t.end();
+});
+
+test('short and long', function (t) {
+ t.deepEqual(
+ parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
+ {
+ f : true, p : 555, h : 'localhost',
+ _ : [ 'script.js' ]
+ }
+ );
+ t.end();
+});
--- /dev/null
+var parse = require('../');
+var test = require('tape');
+
+test('whitespace should be whitespace' , function (t) {
+ t.plan(1);
+ var x = parse([ '-x', '\t' ]).x;
+ t.equal(x, '\t');
+});
--- /dev/null
+{
+ "name": "mkdirp",
+ "description": "Recursively mkdir, like `mkdir -p`",
+ "version": "0.5.1",
+ "author": {
+ "name": "James Halliday",
+ "email": "mail@substack.net",
+ "url": "http://substack.net"
+ },
+ "main": "index.js",
+ "keywords": [
+ "mkdir",
+ "directory"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/substack/node-mkdirp.git"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "minimist": "0.0.8"
+ },
+ "devDependencies": {
+ "tap": "1",
+ "mock-fs": "2 >=2.7.0"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "license": "MIT",
+ "gitHead": "d4eff0f06093aed4f387e88e9fc301cb76beedc7",
+ "bugs": {
+ "url": "https://github.com/substack/node-mkdirp/issues"
+ },
+ "homepage": "https://github.com/substack/node-mkdirp#readme",
+ "_id": "mkdirp@0.5.1",
+ "_shasum": "30057438eac6cf7f8c4767f38648d6697d75c903",
+ "_from": "mkdirp@>=0.5.0 <0.6.0",
+ "_npmVersion": "2.9.0",
+ "_nodeVersion": "2.0.0",
+ "_npmUser": {
+ "name": "substack",
+ "email": "substack@gmail.com"
+ },
+ "dist": {
+ "shasum": "30057438eac6cf7f8c4767f38648d6697d75c903",
+ "tarball": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "substack",
+ "email": "mail@substack.net"
+ }
+ ],
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
+}
--- /dev/null
+# mkdirp
+
+Like `mkdir -p`, but in node.js!
+
+[](http://travis-ci.org/substack/node-mkdirp)
+
+# example
+
+## pow.js
+
+```js
+var mkdirp = require('mkdirp');
+
+mkdirp('/tmp/foo/bar/baz', function (err) {
+ if (err) console.error(err)
+ else console.log('pow!')
+});
+```
+
+Output
+
+```
+pow!
+```
+
+And now /tmp/foo/bar/baz exists, huzzah!
+
+# methods
+
+```js
+var mkdirp = require('mkdirp');
+```
+
+## mkdirp(dir, opts, cb)
+
+Create a new directory and any necessary subdirectories at `dir` with octal
+permission string `opts.mode`. If `opts` is a non-object, it will be treated as
+the `opts.mode`.
+
+If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`.
+
+`cb(err, made)` fires with the error or the first directory `made`
+that had to be created, if any.
+
+You can optionally pass in an alternate `fs` implementation by passing in
+`opts.fs`. Your implementation should have `opts.fs.mkdir(path, mode, cb)` and
+`opts.fs.stat(path, cb)`.
+
+## mkdirp.sync(dir, opts)
+
+Synchronously create a new directory and any necessary subdirectories at `dir`
+with octal permission string `opts.mode`. If `opts` is a non-object, it will be
+treated as the `opts.mode`.
+
+If `opts.mode` isn't specified, it defaults to `0777 & (~process.umask())`.
+
+Returns the first directory that had to be created, if any.
+
+You can optionally pass in an alternate `fs` implementation by passing in
+`opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)` and
+`opts.fs.statSync(path)`.
+
+# usage
+
+This package also ships with a `mkdirp` command.
+
+```
+usage: mkdirp [DIR1,DIR2..] {OPTIONS}
+
+ Create each supplied directory including any necessary parent directories that
+ don't yet exist.
+
+ If the directory already exists, do nothing.
+
+OPTIONS are:
+
+ -m, --mode If a directory needs to be created, set the mode as an octal
+ permission string.
+
+```
+
+# install
+
+With [npm](http://npmjs.org) do:
+
+```
+npm install mkdirp
+```
+
+to get the library, or
+
+```
+npm install -g mkdirp
+```
+
+to get the command.
+
+# license
+
+MIT
--- /dev/null
+var mkdirp = require('../').mkdirp;
+var path = require('path');
+var fs = require('fs');
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+var _0744 = parseInt('0744', 8);
+
+var ps = [ '', 'tmp' ];
+
+for (var i = 0; i < 25; i++) {
+ var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ ps.push(dir);
+}
+
+var file = ps.join('/');
+
+test('chmod-pre', function (t) {
+ var mode = _0744
+ mkdirp(file, mode, function (er) {
+ t.ifError(er, 'should not error');
+ fs.stat(file, function (er, stat) {
+ t.ifError(er, 'should exist');
+ t.ok(stat && stat.isDirectory(), 'should be directory');
+ t.equal(stat && stat.mode & _0777, mode, 'should be 0744');
+ t.end();
+ });
+ });
+});
+
+test('chmod', function (t) {
+ var mode = _0755
+ mkdirp(file, mode, function (er) {
+ t.ifError(er, 'should not error');
+ fs.stat(file, function (er, stat) {
+ t.ifError(er, 'should exist');
+ t.ok(stat && stat.isDirectory(), 'should be directory');
+ t.end();
+ });
+ });
+});
--- /dev/null
+var mkdirp = require('../').mkdirp;
+var path = require('path');
+var fs = require('fs');
+var test = require('tap').test;
+var _0755 = parseInt('0755', 8);
+
+var ps = [ '', 'tmp' ];
+
+for (var i = 0; i < 25; i++) {
+ var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ ps.push(dir);
+}
+
+var file = ps.join('/');
+
+// a file in the way
+var itw = ps.slice(0, 3).join('/');
+
+
+test('clobber-pre', function (t) {
+ console.error("about to write to "+itw)
+ fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.');
+
+ fs.stat(itw, function (er, stat) {
+ t.ifError(er)
+ t.ok(stat && stat.isFile(), 'should be file')
+ t.end()
+ })
+})
+
+test('clobber', function (t) {
+ t.plan(2);
+ mkdirp(file, _0755, function (err) {
+ t.ok(err);
+ t.equal(err.code, 'ENOTDIR');
+ t.end();
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('woo', function (t) {
+ t.plan(5);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/tmp/' + [x,y,z].join('/');
+
+ mkdirp(file, _0755, function (err) {
+ t.ifError(err);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ })
+ })
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var test = require('tap').test;
+var mockfs = require('mock-fs');
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('opts.fs', function (t) {
+ t.plan(5);
+
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/beep/boop/' + [x,y,z].join('/');
+ var xfs = mockfs.fs();
+
+ mkdirp(file, { fs: xfs, mode: _0755 }, function (err) {
+ t.ifError(err);
+ xfs.exists(file, function (ex) {
+ t.ok(ex, 'created file');
+ xfs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ });
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var test = require('tap').test;
+var mockfs = require('mock-fs');
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('opts.fs sync', function (t) {
+ t.plan(4);
+
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/beep/boop/' + [x,y,z].join('/');
+ var xfs = mockfs.fs();
+
+ mkdirp.sync(file, { fs: xfs, mode: _0755 });
+ xfs.exists(file, function (ex) {
+ t.ok(ex, 'created file');
+ xfs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('async perm', function (t) {
+ t.plan(5);
+ var file = '/tmp/' + (Math.random() * (1<<30)).toString(16);
+
+ mkdirp(file, _0755, function (err) {
+ t.ifError(err);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ })
+ })
+ });
+});
+
+test('async root perm', function (t) {
+ mkdirp('/tmp', _0755, function (err) {
+ if (err) t.fail(err);
+ t.end();
+ });
+ t.end();
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('sync perm', function (t) {
+ t.plan(4);
+ var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json';
+
+ mkdirp.sync(file, _0755);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ });
+});
+
+test('sync root perm', function (t) {
+ t.plan(3);
+
+ var file = '/tmp';
+ mkdirp.sync(file, _0755);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ })
+ });
+});
--- /dev/null
+var mkdirp = require('../').mkdirp;
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('race', function (t) {
+ t.plan(10);
+ var ps = [ '', 'tmp' ];
+
+ for (var i = 0; i < 25; i++) {
+ var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ ps.push(dir);
+ }
+ var file = ps.join('/');
+
+ var res = 2;
+ mk(file);
+
+ mk(file);
+
+ function mk (file, cb) {
+ mkdirp(file, _0755, function (err) {
+ t.ifError(err);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ })
+ });
+ }
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('rel', function (t) {
+ t.plan(5);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var cwd = process.cwd();
+ process.chdir('/tmp');
+
+ var file = [x,y,z].join('/');
+
+ mkdirp(file, _0755, function (err) {
+ t.ifError(err);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ process.chdir(cwd);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ })
+ })
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var test = require('tap').test;
+
+test('return value', function (t) {
+ t.plan(4);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/tmp/' + [x,y,z].join('/');
+
+ // should return the first dir created.
+ // By this point, it would be profoundly surprising if /tmp didn't
+ // already exist, since every other test makes things in there.
+ mkdirp(file, function (err, made) {
+ t.ifError(err);
+ t.equal(made, '/tmp/' + x);
+ mkdirp(file, function (err, made) {
+ t.ifError(err);
+ t.equal(made, null);
+ });
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var test = require('tap').test;
+
+test('return value', function (t) {
+ t.plan(2);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/tmp/' + [x,y,z].join('/');
+
+ // should return the first dir created.
+ // By this point, it would be profoundly surprising if /tmp didn't
+ // already exist, since every other test makes things in there.
+ // Note that this will throw on failure, which will fail the test.
+ var made = mkdirp.sync(file);
+ t.equal(made, '/tmp/' + x);
+
+ // making the same file again should have no effect.
+ made = mkdirp.sync(file);
+ t.equal(made, null);
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var test = require('tap').test;
+var _0755 = parseInt('0755', 8);
+
+test('root', function (t) {
+ // '/' on unix, 'c:/' on windows.
+ var file = path.resolve('/');
+
+ mkdirp(file, _0755, function (err) {
+ if (err) throw err
+ fs.stat(file, function (er, stat) {
+ if (er) throw er
+ t.ok(stat.isDirectory(), 'target is a directory');
+ t.end();
+ })
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('sync', function (t) {
+ t.plan(4);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/tmp/' + [x,y,z].join('/');
+
+ try {
+ mkdirp.sync(file, _0755);
+ } catch (err) {
+ t.fail(err);
+ return t.end();
+ }
+
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0755);
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('implicit mode from umask', function (t) {
+ t.plan(5);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/tmp/' + [x,y,z].join('/');
+
+ mkdirp(file, function (err) {
+ t.ifError(err);
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, _0777 & (~process.umask()));
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ })
+ });
+});
--- /dev/null
+var mkdirp = require('../');
+var path = require('path');
+var fs = require('fs');
+var exists = fs.exists || path.exists;
+var test = require('tap').test;
+var _0777 = parseInt('0777', 8);
+var _0755 = parseInt('0755', 8);
+
+test('umask sync modes', function (t) {
+ t.plan(4);
+ var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+ var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
+
+ var file = '/tmp/' + [x,y,z].join('/');
+
+ try {
+ mkdirp.sync(file);
+ } catch (err) {
+ t.fail(err);
+ return t.end();
+ }
+
+ exists(file, function (ex) {
+ t.ok(ex, 'file created');
+ fs.stat(file, function (err, stat) {
+ t.ifError(err);
+ t.equal(stat.mode & _0777, (_0777 & (~process.umask())));
+ t.ok(stat.isDirectory(), 'target not a directory');
+ });
+ });
+});
--- /dev/null
+{
+ "asi": true,
+ "node": true,
+ "strict": true
+}
--- /dev/null
+components
+node_modules
+test
+.gitignore
+.travis.yml
+component.json
--- /dev/null
+Copyright (c) 2014 Forbes Lindesay
+
+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.
--- /dev/null
+<a href="https://promisesaplus.com/"><img src="https://promisesaplus.com/assets/logo-small.png" align="right" /></a>
+# promise
+
+This is a simple implementation of Promises. It is a super set of ES6 Promises designed to have readable, performant code and to provide just the extensions that are absolutely necessary for using promises today.
+
+For detailed tutorials on its use, see www.promisejs.org
+
+[](https://travis-ci.org/then/promise)
+[](https://gemnasium.com/then/promise)
+[](https://www.npmjs.org/package/promise)
+
+## Installation
+
+**Server:**
+
+ $ npm install promise
+
+**Client:**
+
+You can use browserify on the client, or use the pre-compiled script that acts as a polyfill.
+
+```html
+<script src="https://www.promisejs.org/polyfills/promise-6.1.0.js"></script>
+```
+
+Note that the [es5-shim](https://github.com/es-shims/es5-shim) must be loaded before this library to support browsers pre IE9.
+
+```html
+<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/3.4.0/es5-shim.min.js"></script>
+```
+
+## Usage
+
+The example below shows how you can load the promise library (in a way that works on both client and server). It then demonstrates creating a promise from scratch. You simply call `new Promise(fn)`. There is a complete specification for what is returned by this method in [Promises/A+](http://promises-aplus.github.com/promises-spec/).
+
+```javascript
+var Promise = require('promise');
+
+var promise = new Promise(function (resolve, reject) {
+ get('http://www.google.com', function (err, res) {
+ if (err) reject(err);
+ else resolve(res);
+ });
+});
+```
+
+## API
+
+Before all examples, you will need:
+
+```js
+var Promise = require('promise');
+```
+
+### new Promise(resolver)
+
+This creates and returns a new promise. `resolver` must be a function. The `resolver` function is passed two arguments:
+
+ 1. `resolve` should be called with a single argument. If it is called with a non-promise value then the promise is fulfilled with that value. If it is called with a promise (A) then the returned promise takes on the state of that new promise (A).
+ 2. `reject` should be called with a single argument. The returned promise will be rejected with that argument.
+
+### Static Functions
+
+ These methods are invoked by calling `Promise.methodName`.
+
+#### Promise.resolve(value)
+
+(deprecated aliases: `Promise.from(value)`, `Promise.cast(value)`)
+
+Converts values and foreign promises into Promises/A+ promises. If you pass it a value then it returns a Promise for that value. If you pass it something that is close to a promise (such as a jQuery attempt at a promise) it returns a Promise that takes on the state of `value` (rejected or fulfilled).
+
+#### Promise.all(array)
+
+Returns a promise for an array. If it is called with a single argument that `Array.isArray` then this returns a promise for a copy of that array with any promises replaced by their fulfilled values. Otherwise it returns a promise for an array that conatins its arguments, except with promises replaced by their resolution values. e.g.
+
+```js
+Promise.all([Promise.resolve('a'), 'b', Promise.resolve('c')])
+ .then(function (res) {
+ assert(res[0] === 'a')
+ assert(res[1] === 'b')
+ assert(res[2] === 'c')
+ })
+
+Promise.all(Promise.resolve('a'), 'b', Promise.resolve('c'))
+ .then(function (res) {
+ assert(res[0] === 'a')
+ assert(res[1] === 'b')
+ assert(res[2] === 'c')
+ })
+```
+
+#### Promise.denodeify(fn)
+
+_Non Standard_
+
+Takes a function which accepts a node style callback and returns a new function that returns a promise instead.
+
+e.g.
+
+```javascript
+var fs = require('fs')
+
+var read = Promise.denodeify(fs.readFile)
+var write = Promise.denodeify(fs.writeFile)
+
+var p = read('foo.json', 'utf8')
+ .then(function (str) {
+ return write('foo.json', JSON.stringify(JSON.parse(str), null, ' '), 'utf8')
+ })
+```
+
+#### Promise.nodeify(fn)
+
+_Non Standard_
+
+The twin to `denodeify` is useful when you want to export an API that can be used by people who haven't learnt about the brilliance of promises yet.
+
+```javascript
+module.exports = Promise.nodeify(awesomeAPI)
+function awesomeAPI(a, b) {
+ return download(a, b)
+}
+```
+
+If the last argument passed to `module.exports` is a function, then it will be treated like a node.js callback and not parsed on to the child function, otherwise the API will just return a promise.
+
+### Prototype Methods
+
+These methods are invoked on a promise instance by calling `myPromise.methodName`
+
+### Promise#then(onFulfilled, onRejected)
+
+This method follows the [Promises/A+ spec](http://promises-aplus.github.io/promises-spec/). It explains things very clearly so I recommend you read it.
+
+Either `onFulfilled` or `onRejected` will be called and they will not be called more than once. They will be passed a single argument and will always be called asynchronously (in the next turn of the event loop).
+
+If the promise is fulfilled then `onFulfilled` is called. If the promise is rejected then `onRejected` is called.
+
+The call to `.then` also returns a promise. If the handler that is called returns a promise, the promise returned by `.then` takes on the state of that returned promise. If the handler that is called returns a value that is not a promise, the promise returned by `.then` will be fulfilled with that value. If the handler that is called throws an exception then the promise returned by `.then` is rejected with that exception.
+
+#### Promise#catch(onRejected)
+
+Sugar for `Promise#then(null, onRejected)`, to mirror `catch` in synchronous code.
+
+#### Promise#done(onFulfilled, onRejected)
+
+_Non Standard_
+
+The same semantics as `.then` except that it does not return a promise and any exceptions are re-thrown so that they can be logged (crashing the application in non-browser environments)
+
+#### Promise#nodeify(callback)
+
+_Non Standard_
+
+If `callback` is `null` or `undefined` it just returns `this`. If `callback` is a function it is called with rejection reason as the first argument and result as the second argument (as per the node.js convention).
+
+This lets you write API functions that look like:
+
+```javascript
+function awesomeAPI(foo, bar, callback) {
+ return internalAPI(foo, bar)
+ .then(parseResult)
+ .then(null, retryErrors)
+ .nodeify(callback)
+}
+```
+
+People who use typical node.js style callbacks will be able to just pass a callback and get the expected behavior. The enlightened people can not pass a callback and will get awesome promises.
+
+## Extending Promises
+
+ There are three options for extending the promises created by this library.
+
+### Inheritance
+
+ You can use inheritance if you want to create your own complete promise library with this as your basic starting point, perfect if you have lots of cool features you want to add. Here is an example of a promise library called `Awesome`, which is built on top of `Promise` correctly.
+
+```javascript
+var Promise = require('promise');
+function Awesome(fn) {
+ if (!(this instanceof Awesome)) return new Awesome(fn);
+ Promise.call(this, fn);
+}
+Awesome.prototype = Object.create(Promise.prototype);
+Awesome.prototype.constructor = Awesome;
+
+//Awesome extension
+Awesome.prototype.spread = function (cb) {
+ return this.then(function (arr) {
+ return cb.apply(this, arr);
+ })
+};
+```
+
+ N.B. if you fail to set the prototype and constructor properly or fail to do Promise.call, things can fail in really subtle ways.
+
+### Wrap
+
+ This is the nuclear option, for when you want to start from scratch. It ensures you won't be impacted by anyone who is extending the prototype (see below).
+
+```javascript
+function Uber(fn) {
+ if (!(this instanceof Uber)) return new Uber(fn);
+ var _prom = new Promise(fn);
+ this.then = _prom.then;
+}
+
+Uber.prototype.spread = function (cb) {
+ return this.then(function (arr) {
+ return cb.apply(this, arr);
+ })
+};
+```
+
+### Extending the Prototype
+
+ In general, you should never extend the prototype of this promise implimenation because your extensions could easily conflict with someone elses extensions. However, this organisation will host a library of extensions which do not conflict with each other, so you can safely enable any of those. If you think of an extension that we don't provide and you want to write it, submit an issue on this repository and (if I agree) I'll set you up with a repository and give you permission to commit to it.
+
+## License
+
+ MIT
--- /dev/null
+'use strict';
+
+module.exports = require('./lib/core.js');
+
+console.error('require("promise/core") is deprecated, use require("promise/lib/core") instead.');
--- /dev/null
+'use strict';
+
+module.exports = require('./lib/core.js')
+require('./lib/done.js')
+require('./lib/es6-extensions.js')
+require('./lib/node-extensions.js')
\ No newline at end of file
--- /dev/null
+'use strict';
+
+var asap = require('asap')
+
+module.exports = Promise;
+function Promise(fn) {
+ if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
+ if (typeof fn !== 'function') throw new TypeError('not a function')
+ var state = null
+ var value = null
+ var deferreds = []
+ var self = this
+
+ this.then = function(onFulfilled, onRejected) {
+ return new self.constructor(function(resolve, reject) {
+ handle(new Handler(onFulfilled, onRejected, resolve, reject))
+ })
+ }
+
+ function handle(deferred) {
+ if (state === null) {
+ deferreds.push(deferred)
+ return
+ }
+ asap(function() {
+ var cb = state ? deferred.onFulfilled : deferred.onRejected
+ if (cb === null) {
+ (state ? deferred.resolve : deferred.reject)(value)
+ return
+ }
+ var ret
+ try {
+ ret = cb(value)
+ }
+ catch (e) {
+ deferred.reject(e)
+ return
+ }
+ deferred.resolve(ret)
+ })
+ }
+
+ function resolve(newValue) {
+ try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
+ if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
+ var then = newValue.then
+ if (typeof then === 'function') {
+ doResolve(then.bind(newValue), resolve, reject)
+ return
+ }
+ }
+ state = true
+ value = newValue
+ finale()
+ } catch (e) { reject(e) }
+ }
+
+ function reject(newValue) {
+ state = false
+ value = newValue
+ finale()
+ }
+
+ function finale() {
+ for (var i = 0, len = deferreds.length; i < len; i++)
+ handle(deferreds[i])
+ deferreds = null
+ }
+
+ doResolve(fn, resolve, reject)
+}
+
+
+function Handler(onFulfilled, onRejected, resolve, reject){
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null
+ this.resolve = resolve
+ this.reject = reject
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, onFulfilled, onRejected) {
+ var done = false;
+ try {
+ fn(function (value) {
+ if (done) return
+ done = true
+ onFulfilled(value)
+ }, function (reason) {
+ if (done) return
+ done = true
+ onRejected(reason)
+ })
+ } catch (ex) {
+ if (done) return
+ done = true
+ onRejected(ex)
+ }
+}
--- /dev/null
+'use strict';
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+Promise.prototype.done = function (onFulfilled, onRejected) {
+ var self = arguments.length ? this.then.apply(this, arguments) : this
+ self.then(null, function (err) {
+ asap(function () {
+ throw err
+ })
+ })
+}
\ No newline at end of file
--- /dev/null
+'use strict';
+
+//This file contains the ES6 extensions to the core Promises/A+ API
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+
+/* Static Functions */
+
+function ValuePromise(value) {
+ this.then = function (onFulfilled) {
+ if (typeof onFulfilled !== 'function') return this
+ return new Promise(function (resolve, reject) {
+ asap(function () {
+ try {
+ resolve(onFulfilled(value))
+ } catch (ex) {
+ reject(ex);
+ }
+ })
+ })
+ }
+}
+ValuePromise.prototype = Promise.prototype
+
+var TRUE = new ValuePromise(true)
+var FALSE = new ValuePromise(false)
+var NULL = new ValuePromise(null)
+var UNDEFINED = new ValuePromise(undefined)
+var ZERO = new ValuePromise(0)
+var EMPTYSTRING = new ValuePromise('')
+
+Promise.resolve = function (value) {
+ if (value instanceof Promise) return value
+
+ if (value === null) return NULL
+ if (value === undefined) return UNDEFINED
+ if (value === true) return TRUE
+ if (value === false) return FALSE
+ if (value === 0) return ZERO
+ if (value === '') return EMPTYSTRING
+
+ if (typeof value === 'object' || typeof value === 'function') {
+ try {
+ var then = value.then
+ if (typeof then === 'function') {
+ return new Promise(then.bind(value))
+ }
+ } catch (ex) {
+ return new Promise(function (resolve, reject) {
+ reject(ex)
+ })
+ }
+ }
+
+ return new ValuePromise(value)
+}
+
+Promise.all = function (arr) {
+ var args = Array.prototype.slice.call(arr)
+
+ return new Promise(function (resolve, reject) {
+ if (args.length === 0) return resolve([])
+ var remaining = args.length
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then
+ if (typeof then === 'function') {
+ then.call(val, function (val) { res(i, val) }, reject)
+ return
+ }
+ }
+ args[i] = val
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex)
+ }
+ }
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i])
+ }
+ })
+}
+
+Promise.reject = function (value) {
+ return new Promise(function (resolve, reject) {
+ reject(value);
+ });
+}
+
+Promise.race = function (values) {
+ return new Promise(function (resolve, reject) {
+ values.forEach(function(value){
+ Promise.resolve(value).then(resolve, reject);
+ })
+ });
+}
+
+/* Prototype Methods */
+
+Promise.prototype['catch'] = function (onRejected) {
+ return this.then(null, onRejected);
+}
--- /dev/null
+'use strict';
+
+//This file contains then/promise specific extensions that are only useful for node.js interop
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+
+/* Static Functions */
+
+Promise.denodeify = function (fn, argumentCount) {
+ argumentCount = argumentCount || Infinity
+ return function () {
+ var self = this
+ var args = Array.prototype.slice.call(arguments)
+ return new Promise(function (resolve, reject) {
+ while (args.length && args.length > argumentCount) {
+ args.pop()
+ }
+ args.push(function (err, res) {
+ if (err) reject(err)
+ else resolve(res)
+ })
+ var res = fn.apply(self, args)
+ if (res && (typeof res === 'object' || typeof res === 'function') && typeof res.then === 'function') {
+ resolve(res)
+ }
+ })
+ }
+}
+Promise.nodeify = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments)
+ var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null
+ var ctx = this
+ try {
+ return fn.apply(this, arguments).nodeify(callback, ctx)
+ } catch (ex) {
+ if (callback === null || typeof callback == 'undefined') {
+ return new Promise(function (resolve, reject) { reject(ex) })
+ } else {
+ asap(function () {
+ callback.call(ctx, ex)
+ })
+ }
+ }
+ }
+}
+
+Promise.prototype.nodeify = function (callback, ctx) {
+ if (typeof callback != 'function') return this
+
+ this.then(function (value) {
+ asap(function () {
+ callback.call(ctx, null, value)
+ })
+ }, function (err) {
+ asap(function () {
+ callback.call(ctx, err)
+ })
+ })
+}
--- /dev/null
+
+Copyright 2009–2013 Contributors. 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.
+
--- /dev/null
+
+# ASAP
+
+This `asap` CommonJS package contains a single `asap` module that
+exports a single `asap` function that executes a function **as soon as
+possible**.
+
+```javascript
+asap(function () {
+ // ...
+});
+```
+
+More formally, ASAP provides a fast event queue that will execute tasks
+until it is empty before yielding to the JavaScript engine's underlying
+event-loop. When the event queue becomes non-empty, ASAP schedules a
+flush event, preferring for that event to occur before the JavaScript
+engine has an opportunity to perform IO tasks or rendering, thus making
+the first task and subsequent tasks semantically indistinguishable.
+ASAP uses a variety of techniques to preserve this invariant on
+different versions of browsers and NodeJS.
+
+By design, ASAP can starve the event loop on the theory that, if there
+is enough work to be done synchronously, albeit in separate events, long
+enough to starve input or output, it is a strong indicator that the
+program needs to push back on scheduling more work.
+
+Take care. ASAP can sustain infinite recursive calls indefinitely
+without warning. This is behaviorally equivalent to an infinite loop.
+It will not halt from a stack overflow, but it *will* chew through
+memory (which is an oddity I cannot explain at this time). Just as with
+infinite loops, you can monitor a Node process for this behavior with a
+heart-beat signal. As with infinite loops, a very small amount of
+caution goes a long way to avoiding problems.
+
+```javascript
+function loop() {
+ asap(loop);
+}
+loop();
+```
+
+ASAP is distinct from `setImmediate` in that it does not suffer the
+overhead of returning a handle and being possible to cancel. For a
+`setImmediate` shim, consider [setImmediate][].
+
+[setImmediate]: https://github.com/noblejs/setimmediate
+
+If a task throws an exception, it will not interrupt the flushing of
+high-priority tasks. The exception will be postponed to a later,
+low-priority event to avoid slow-downs, when the underlying JavaScript
+engine will treat it as it does any unhandled exception.
+
+## Heritage
+
+ASAP has been factored out of the [Q][] asynchronous promise library.
+It originally had a naïve implementation in terms of `setTimeout`, but
+[Malte Ubl][NonBlocking] provided an insight that `postMessage` might be
+useful for creating a high-priority, no-delay event dispatch hack.
+Since then, Internet Explorer proposed and implemented `setImmediate`.
+Robert Kratić began contributing to Q by measuring the performance of
+the internal implementation of `asap`, paying particular attention to
+error recovery. Domenic, Robert, and I collectively settled on the
+current strategy of unrolling the high-priority event queue internally
+regardless of what strategy we used to dispatch the potentially
+lower-priority flush event. Domenic went on to make ASAP cooperate with
+NodeJS domains.
+
+[Q]: https://github.com/kriskowal/q
+[NonBlocking]: http://www.nonblocking.io/2011/06/windownexttick.html
+
+For further reading, Nicholas Zakas provided a thorough article on [The
+Case for setImmediate][NCZ].
+
+[NCZ]: http://www.nczonline.net/blog/2013/07/09/the-case-for-setimmediate/
+
+## License
+
+Copyright 2009-2013 by Contributors
+MIT License (enclosed)
+
--- /dev/null
+
+// Use the fastest possible means to execute a task in a future turn
+// of the event loop.
+
+// linked list of tasks (single, with head node)
+var head = {task: void 0, next: null};
+var tail = head;
+var flushing = false;
+var requestFlush = void 0;
+var isNodeJS = false;
+
+function flush() {
+ /* jshint loopfunc: true */
+
+ while (head.next) {
+ head = head.next;
+ var task = head.task;
+ head.task = void 0;
+ var domain = head.domain;
+
+ if (domain) {
+ head.domain = void 0;
+ domain.enter();
+ }
+
+ try {
+ task();
+
+ } catch (e) {
+ if (isNodeJS) {
+ // In node, uncaught exceptions are considered fatal errors.
+ // Re-throw them synchronously to interrupt flushing!
+
+ // Ensure continuation if the uncaught exception is suppressed
+ // listening "uncaughtException" events (as domains does).
+ // Continue in next event to avoid tick recursion.
+ if (domain) {
+ domain.exit();
+ }
+ setTimeout(flush, 0);
+ if (domain) {
+ domain.enter();
+ }
+
+ throw e;
+
+ } else {
+ // In browsers, uncaught exceptions are not fatal.
+ // Re-throw them asynchronously to avoid slow-downs.
+ setTimeout(function() {
+ throw e;
+ }, 0);
+ }
+ }
+
+ if (domain) {
+ domain.exit();
+ }
+ }
+
+ flushing = false;
+}
+
+if (typeof process !== "undefined" && process.nextTick) {
+ // Node.js before 0.9. Note that some fake-Node environments, like the
+ // Mocha test runner, introduce a `process` global without a `nextTick`.
+ isNodeJS = true;
+
+ requestFlush = function () {
+ process.nextTick(flush);
+ };
+
+} else if (typeof setImmediate === "function") {
+ // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
+ if (typeof window !== "undefined") {
+ requestFlush = setImmediate.bind(window, flush);
+ } else {
+ requestFlush = function () {
+ setImmediate(flush);
+ };
+ }
+
+} else if (typeof MessageChannel !== "undefined") {
+ // modern browsers
+ // http://www.nonblocking.io/2011/06/windownexttick.html
+ var channel = new MessageChannel();
+ channel.port1.onmessage = flush;
+ requestFlush = function () {
+ channel.port2.postMessage(0);
+ };
+
+} else {
+ // old browsers
+ requestFlush = function () {
+ setTimeout(flush, 0);
+ };
+}
+
+function asap(task) {
+ tail = tail.next = {
+ task: task,
+ domain: isNodeJS && process.domain,
+ next: null
+ };
+
+ if (!flushing) {
+ flushing = true;
+ requestFlush();
+ }
+};
+
+module.exports = asap;
+
--- /dev/null
+{
+ "name": "asap",
+ "version": "1.0.0",
+ "description": "High-priority task queue for Node.js and browsers",
+ "keywords": [
+ "event",
+ "task",
+ "queue"
+ ],
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/kriskowal/asap/raw/master/LICENSE.md"
+ }
+ ],
+ "main": "asap",
+ "_id": "asap@1.0.0",
+ "dist": {
+ "shasum": "b2a45da5fdfa20b0496fc3768cc27c12fa916a7d",
+ "tarball": "http://registry.npmjs.org/asap/-/asap-1.0.0.tgz"
+ },
+ "_from": "asap@>=1.0.0 <1.1.0",
+ "_npmVersion": "1.2.15",
+ "_npmUser": {
+ "name": "kriskowal",
+ "email": "kris.kowal@cixar.com"
+ },
+ "maintainers": [
+ {
+ "name": "kriskowal",
+ "email": "kris.kowal@cixar.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "b2a45da5fdfa20b0496fc3768cc27c12fa916a7d",
+ "_resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz"
+}
--- /dev/null
+{
+ "name": "promise",
+ "version": "6.1.0",
+ "description": "Bare bones Promises/A+ implementation",
+ "main": "index.js",
+ "scripts": {
+ "test": "mocha --timeout 200 --slow 99999",
+ "test-resolve": "mocha test/resolver-tests.js -R spec --timeout 200 --slow 999999",
+ "test-extensions": "mocha test/extensions-tests.js -R spec --timeout 200 --slow 999999"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/then/promise.git"
+ },
+ "author": {
+ "name": "ForbesLindesay"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "promises-aplus-tests": "*",
+ "better-assert": "*",
+ "mocha": "*"
+ },
+ "dependencies": {
+ "asap": "~1.0.0"
+ },
+ "gitHead": "c627c0cc1f886ed3aa26edec97838ec9e7dd858b",
+ "bugs": {
+ "url": "https://github.com/then/promise/issues"
+ },
+ "homepage": "https://github.com/then/promise",
+ "_id": "promise@6.1.0",
+ "_shasum": "2ce729f6b94b45c26891ad0602c5c90e04c6eef6",
+ "_from": "promise@>=6.0.1 <7.0.0",
+ "_npmVersion": "1.5.0-alpha-4",
+ "_npmUser": {
+ "name": "forbeslindesay",
+ "email": "forbes@lindesay.co.uk"
+ },
+ "maintainers": [
+ {
+ "name": "forbeslindesay",
+ "email": "forbes@lindesay.co.uk"
+ },
+ {
+ "name": "nathan7",
+ "email": "nathan@nathan7.eu"
+ }
+ ],
+ "dist": {
+ "shasum": "2ce729f6b94b45c26891ad0602c5c90e04c6eef6",
+ "tarball": "http://registry.npmjs.org/promise/-/promise-6.1.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz"
+}
--- /dev/null
+// should work in any browser without browserify
+
+if (typeof Promise.prototype.done !== 'function') {
+ Promise.prototype.done = function (onFulfilled, onRejected) {
+ var self = arguments.length ? this.then.apply(this, arguments) : this
+ self.then(null, function (err) {
+ setTimeout(function () {
+ throw err
+ }, 0)
+ })
+ }
+}
\ No newline at end of file
--- /dev/null
+// not "use strict" so we can declare global "Promise"
+
+var asap = require('asap');
+
+if (typeof Promise === 'undefined') {
+ Promise = require('./lib/core.js')
+ require('./lib/es6-extensions.js')
+}
+
+require('./polyfill-done.js');
--- /dev/null
+{
+ "env": {
+ "node": true
+ },
+ "rules": {
+ // 2-space indentation
+ "indent": [2, 2],
+ // Disallow semi-colons, unless needed to disambiguate statement
+ "semi": [2, "never"],
+ // Require strings to use single quotes
+ "quotes": [2, "single"],
+ // Require curly braces for all control statements
+ "curly": 2,
+ // Disallow using variables and functions before they've been defined
+ "no-use-before-define": 2,
+ // Allow any case for variable naming
+ "camelcase": 0,
+ // Disallow unused variables, except as function arguments
+ "no-unused-vars": [2, {"args":"none"}],
+ // Allow leading underscores for method names
+ // REASON: we use underscores to denote private methods
+ "no-underscore-dangle": 0,
+ // Allow multi spaces around operators since they are
+ // used for alignment. This is not consistent in the
+ // code.
+ "no-multi-spaces": 0,
+ // Style rule is: most objects use { beforeColon: false, afterColon: true }, unless aligning which uses:
+ //
+ // {
+ // beforeColon : true,
+ // afterColon : true
+ // }
+ //
+ // eslint can't handle this, so the check is disabled.
+ "key-spacing": 0,
+ // Allow shadowing vars in outer scope (needs discussion)
+ "no-shadow": 0
+ }
+}
--- /dev/null
+coverage
+tests
+node_modules
--- /dev/null
+language: node_js
+node_js:
+ - "io.js"
+ - "0.12"
+ - "0.10"
+after_script: ./node_modules/.bin/istanbul cover ./node_modules/tape/bin/tape tests/test-*.js --report lcovonly && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js --verbose
+webhooks:
+ urls: https://webhooks.gitter.im/e/237280ed4796c19cc626
+ on_success: change # options: [always|never|change] default: always
+ on_failure: always # options: [always|never|change] default: always
+ on_start: false # default: false
+sudo: false
--- /dev/null
+## Change Log
+
+### v2.55.0 (2015/04/05)
+- [#1520](https://github.com/request/request/pull/1520) Refactor defaults (@simov)
+- [#1525](https://github.com/request/request/pull/1525) Delete request headers with undefined value. (@froatsnook)
+- [#1521](https://github.com/request/request/pull/1521) Add promise tests (@simov)
+- [#1518](https://github.com/request/request/pull/1518) Fix defaults (@simov)
+- [#1515](https://github.com/request/request/pull/1515) Allow static invoking of convenience methods (@simov)
+- [#1505](https://github.com/request/request/pull/1505) Fix multipart boundary extraction regexp (@simov)
+- [#1510](https://github.com/request/request/pull/1510) Fix basic auth form data (@simov)
+
+### v2.54.0 (2015/03/24)
+- [#1501](https://github.com/request/request/pull/1501) HTTP Archive 1.2 support (@ahmadnassri)
+- [#1486](https://github.com/request/request/pull/1486) Add a test for the forever agent (@akshayp)
+- [#1500](https://github.com/request/request/pull/1500) Adding handling for no auth method and null bearer (@philberg)
+- [#1498](https://github.com/request/request/pull/1498) Add table of contents in readme (@simov)
+- [#1477](https://github.com/request/request/pull/1477) Add support for qs options via qsOptions key (@simov)
+- [#1496](https://github.com/request/request/pull/1496) Parameters encoded to base 64 should be decoded as UTF-8, not ASCII. (@albanm)
+- [#1494](https://github.com/request/request/pull/1494) Update eslint (@froatsnook)
+- [#1474](https://github.com/request/request/pull/1474) Require Colon in Basic Auth (@erykwalder)
+- [#1481](https://github.com/request/request/pull/1481) Fix baseUrl and redirections. (@burningtree)
+- [#1469](https://github.com/request/request/pull/1469) Feature/base url (@froatsnook)
+- [#1459](https://github.com/request/request/pull/1459) Add option to time request/response cycle (including rollup of redirects) (@aaron-em)
+- [#1468](https://github.com/request/request/pull/1468) Re-enable io.js/node 0.12 build (@simov, @BBB)
+- [#1442](https://github.com/request/request/pull/1442) Fixed the issue with strictSSL tests on 0.12 & io.js by explicitly setting a cipher that matches the cert. (@BBB, @nicolasmccurdy, @simov, @0x4139)
+- [#1460](https://github.com/request/request/pull/1460) localAddress or proxy config is lost when redirecting (@simov, @0x4139)
+- [#1453](https://github.com/request/request/pull/1453) Test on Node.js 0.12 and io.js with allowed failures (@nicolasmccurdy)
+- [#1426](https://github.com/request/request/pull/1426) Fixing tests to pass on io.js and node 0.12 (only test-https.js stiff failing) (@mikeal)
+- [#1446](https://github.com/request/request/pull/1446) Missing HTTP referer header with redirects Fixes #1038 (@simov, @guimonz)
+- [#1428](https://github.com/request/request/pull/1428) Deprecate Node v0.8.x (@nylen)
+- [#1436](https://github.com/request/request/pull/1436) Add ability to set a requester without setting default options (@tikotzky)
+- [#1435](https://github.com/request/request/pull/1435) dry up verb methods (@sethpollack)
+- [#1423](https://github.com/request/request/pull/1423) Allow fully qualified multipart content-type header (@simov)
+- [#1430](https://github.com/request/request/pull/1430) Fix recursive requester (@tikotzky)
+- [#1429](https://github.com/request/request/pull/1429) Throw error when making HEAD request with a body (@tikotzky)
+- [#1419](https://github.com/request/request/pull/1419) Add note that the project is broken in 0.12.x (@nylen)
+- [#1413](https://github.com/request/request/pull/1413) Fix basic auth (@simov)
+- [#1397](https://github.com/request/request/pull/1397) Improve pipe-from-file tests (@nylen)
+
+### v2.53.0 (2015/02/02)
+- [#1396](https://github.com/request/request/pull/1396) Do not rfc3986 escape JSON bodies (@nylen, @simov)
+- [#1392](https://github.com/request/request/pull/1392) Improve `timeout` option description (@watson)
+
+### v2.52.0 (2015/02/02)
+- [#1383](https://github.com/request/request/pull/1383) Add missing HTTPS options that were not being passed to tunnel (@brichard19) (@nylen, @brichard19)
+- [#1388](https://github.com/request/request/pull/1388) Upgrade mime-types package version (@roderickhsiao)
+- [#1389](https://github.com/request/request/pull/1389) Revise Setup Tunnel Function (@seanstrom)
+- [#1374](https://github.com/request/request/pull/1374) Allow explicitly disabling tunneling for proxied https destinations (@nylen)
+- [#1376](https://github.com/request/request/pull/1376) Use karma-browserify for tests. Add browser test coverage reporter. (@eiriksm)
+- [#1366](https://github.com/request/request/pull/1366) Refactor OAuth into separate module (@simov)
+- [#1373](https://github.com/request/request/pull/1373) Rewrite tunnel test to be pure Node.js (@nylen)
+- [#1371](https://github.com/request/request/pull/1371) Upgrade test reporter (@nylen)
+- [#1360](https://github.com/request/request/pull/1360) Refactor basic, bearer, digest auth logic into separate class (@simov)
+- [#1354](https://github.com/request/request/pull/1354) Remove circular dependency from debugging code (@nylen)
+- [#1351](https://github.com/request/request/pull/1351) Move digest auth into private prototype method (@simov)
+- [#1352](https://github.com/request/request/pull/1352) Update hawk dependency to ~2.3.0 (@mridgway)
+- [#1353](https://github.com/request/request/pull/1353) Correct travis-ci badge (@dogancelik)
+- [#1349](https://github.com/request/request/pull/1349) Make sure we return on errored browser requests. (@eiriksm)
+- [#1346](https://github.com/request/request/pull/1346) getProxyFromURI Extraction Refactor (@seanstrom)
+- [#1337](https://github.com/request/request/pull/1337) Standardize test ports on 6767 (@nylen)
+- [#1341](https://github.com/request/request/pull/1341) Emit FormData error events as Request error events (@nylen, @rwky)
+- [#1343](https://github.com/request/request/pull/1343) Clean up readme badges, and add Travis and Coveralls badges (@nylen)
+- [#1345](https://github.com/request/request/pull/1345) Update README.md (@Aaron-Hartwig)
+- [#1338](https://github.com/request/request/pull/1338) Always wait for server.close() callback in tests (@nylen)
+- [#1342](https://github.com/request/request/pull/1342) Add mock https server and redo start of browser tests for this purpose. (@eiriksm)
+- [#1339](https://github.com/request/request/pull/1339) Improve auth docs (@nylen)
+- [#1335](https://github.com/request/request/pull/1335) Add support for OAuth plaintext signature method (@simov)
+- [#1332](https://github.com/request/request/pull/1332) Add clean script to remove test-browser.js after the tests run (@seanstrom)
+- [#1327](https://github.com/request/request/pull/1327) Fix errors generating coverage reports. (@nylen)
+- [#1330](https://github.com/request/request/pull/1330) Return empty buffer upon empty response body and encoding is set to null (@seanstrom)
+- [#1326](https://github.com/request/request/pull/1326) Use faster container-based infrastructure on Travis (@nylen)
+- [#1315](https://github.com/request/request/pull/1315) Implement rfc3986 option (@simov)
+- [#1314](https://github.com/request/request/pull/1314) Detect urlencoded form data header via regex (@simov)
+- [#1317](https://github.com/request/request/pull/1317) Improve OAuth1.0 server side flow example (@simov)
+
+### v2.51.0 (2014/12/10)
+- [#1310](https://github.com/request/request/pull/1310) Revert changes introduced in https://github.com/request/request/pull/1282 (@simov)
+
+### v2.50.0 (2014/12/09)
+- [#1308](https://github.com/request/request/pull/1308) Add browser test to keep track of browserify compability. (@eiriksm)
+- [#1299](https://github.com/request/request/pull/1299) Add optional support for jsonReviver (@poislagarde)
+- [#1277](https://github.com/request/request/pull/1277) Add Coveralls configuration (@simov)
+- [#1307](https://github.com/request/request/pull/1307) Upgrade form-data, add back browserify compability. Fixes #455. (@eiriksm)
+- [#1305](https://github.com/request/request/pull/1305) Fix typo in README.md (@LewisJEllis)
+- [#1288](https://github.com/request/request/pull/1288) Update README.md to explain custom file use case (@cliffcrosland)
+
+### v2.49.0 (2014/11/28)
+- [#1295](https://github.com/request/request/pull/1295) fix(proxy): no-proxy false positive (@oliamb)
+- [#1292](https://github.com/request/request/pull/1292) Upgrade `caseless` to 0.8.1 (@mmalecki)
+- [#1276](https://github.com/request/request/pull/1276) Set transfer encoding for multipart/related to chunked by default (@simov)
+- [#1275](https://github.com/request/request/pull/1275) Fix multipart content-type headers detection (@simov)
+- [#1269](https://github.com/request/request/pull/1269) adds streams example for review (@tbuchok)
+- [#1238](https://github.com/request/request/pull/1238) Add examples README.md (@simov)
+
+### v2.48.0 (2014/11/12)
+- [#1263](https://github.com/request/request/pull/1263) Fixed a syntax error / typo in README.md (@xna2)
+- [#1253](https://github.com/request/request/pull/1253) Add multipart chunked flag (@simov, @nylen)
+- [#1251](https://github.com/request/request/pull/1251) Clarify that defaults() does not modify global defaults (@nylen)
+- [#1250](https://github.com/request/request/pull/1250) Improve documentation for pool and maxSockets options (@nylen)
+- [#1237](https://github.com/request/request/pull/1237) Documenting error handling when using streams (@vmattos)
+- [#1244](https://github.com/request/request/pull/1244) Finalize changelog command (@nylen)
+- [#1241](https://github.com/request/request/pull/1241) Fix typo (@alexanderGugel)
+- [#1223](https://github.com/request/request/pull/1223) Show latest version number instead of "upcoming" in changelog (@nylen)
+- [#1236](https://github.com/request/request/pull/1236) Document how to use custom CA in README (#1229) (@hypesystem)
+- [#1228](https://github.com/request/request/pull/1228) Support for oauth with RSA-SHA1 signing (@nylen)
+- [#1216](https://github.com/request/request/pull/1216) Made json and multipart options coexist (@nylen, @simov)
+- [#1225](https://github.com/request/request/pull/1225) Allow header white/exclusive lists in any case. (@RReverser)
+
+### v2.47.0 (2014/10/26)
+- [#1222](https://github.com/request/request/pull/1222) Move from mikeal/request to request/request (@nylen)
+- [#1220](https://github.com/request/request/pull/1220) update qs dependency to 2.3.1 (@FredKSchott)
+- [#1212](https://github.com/request/request/pull/1212) Improve tests/test-timeout.js (@nylen)
+- [#1219](https://github.com/request/request/pull/1219) remove old globalAgent workaround for node 0.4 (@request)
+- [#1214](https://github.com/request/request/pull/1214) Remove cruft left over from optional dependencies (@nylen)
+- [#1215](https://github.com/request/request/pull/1215) Add proxyHeaderExclusiveList option for proxy-only headers. (@RReverser)
+- [#1211](https://github.com/request/request/pull/1211) Allow 'Host' header instead of 'host' and remember case across redirects (@nylen)
+- [#1208](https://github.com/request/request/pull/1208) Improve release script (@nylen)
+- [#1213](https://github.com/request/request/pull/1213) Support for custom cookie store (@nylen, @mitsuru)
+- [#1197](https://github.com/request/request/pull/1197) Clean up some code around setting the agent (@FredKSchott)
+- [#1209](https://github.com/request/request/pull/1209) Improve multipart form append test (@simov)
+- [#1207](https://github.com/request/request/pull/1207) Update changelog (@nylen)
+- [#1185](https://github.com/request/request/pull/1185) Stream multipart/related bodies (@simov)
+
+### v2.46.0 (2014/10/23)
+- [#1198](https://github.com/request/request/pull/1198) doc for TLS/SSL protocol options (@shawnzhu)
+- [#1200](https://github.com/request/request/pull/1200) Add a Gitter chat badge to README.md (@gitter-badger)
+- [#1196](https://github.com/request/request/pull/1196) Upgrade taper test reporter to v0.3.0 (@nylen)
+- [#1199](https://github.com/request/request/pull/1199) Fix lint error: undeclared var i (@nylen)
+- [#1191](https://github.com/request/request/pull/1191) Move self.proxy decision logic out of init and into a helper (@FredKSchott)
+- [#1190](https://github.com/request/request/pull/1190) Move _buildRequest() logic back into init (@FredKSchott)
+- [#1186](https://github.com/request/request/pull/1186) Support Smarter Unix URL Scheme (@FredKSchott)
+- [#1178](https://github.com/request/request/pull/1178) update form documentation for new usage (@FredKSchott)
+- [#1180](https://github.com/request/request/pull/1180) Enable no-mixed-requires linting rule (@nylen)
+- [#1184](https://github.com/request/request/pull/1184) Don't forward authorization header across redirects to different hosts (@nylen)
+- [#1183](https://github.com/request/request/pull/1183) Correct README about pre and postamble CRLF using multipart and not mult... (@netpoetica)
+- [#1179](https://github.com/request/request/pull/1179) Lint tests directory (@nylen)
+- [#1169](https://github.com/request/request/pull/1169) add metadata for form-data file field (@dotcypress)
+- [#1173](https://github.com/request/request/pull/1173) remove optional dependencies (@seanstrom)
+- [#1165](https://github.com/request/request/pull/1165) Cleanup event listeners and remove function creation from init (@FredKSchott)
+- [#1174](https://github.com/request/request/pull/1174) update the request.cookie docs to have a valid cookie example (@seanstrom)
+- [#1168](https://github.com/request/request/pull/1168) create a detach helper and use detach helper in replace of nextTick (@seanstrom)
+- [#1171](https://github.com/request/request/pull/1171) in post can send form data and use callback (@MiroRadenovic)
+- [#1159](https://github.com/request/request/pull/1159) accept charset for x-www-form-urlencoded content-type (@seanstrom)
+- [#1157](https://github.com/request/request/pull/1157) Update README.md: body with json=true (@Rob--W)
+- [#1164](https://github.com/request/request/pull/1164) Disable tests/test-timeout.js on Travis (@nylen)
+- [#1153](https://github.com/request/request/pull/1153) Document how to run a single test (@nylen)
+- [#1144](https://github.com/request/request/pull/1144) adds documentation for the "response" event within the streaming section (@tbuchok)
+- [#1162](https://github.com/request/request/pull/1162) Update eslintrc file to no longer allow past errors (@FredKSchott)
+- [#1155](https://github.com/request/request/pull/1155) Support/use self everywhere (@seanstrom)
+- [#1161](https://github.com/request/request/pull/1161) fix no-use-before-define lint warnings (@emkay)
+- [#1156](https://github.com/request/request/pull/1156) adding curly brackets to get rid of lint errors (@emkay)
+- [#1151](https://github.com/request/request/pull/1151) Fix localAddress test on OS X (@nylen)
+- [#1145](https://github.com/request/request/pull/1145) documentation: fix outdated reference to setCookieSync old name in README (@FredKSchott)
+- [#1131](https://github.com/request/request/pull/1131) Update pool documentation (@FredKSchott)
+- [#1143](https://github.com/request/request/pull/1143) Rewrite all tests to use tape (@nylen)
+- [#1137](https://github.com/request/request/pull/1137) Add ability to specifiy querystring lib in options. (@jgrund)
+- [#1138](https://github.com/request/request/pull/1138) allow hostname and port in place of host on uri (@cappslock)
+- [#1134](https://github.com/request/request/pull/1134) Fix multiple redirects and `self.followRedirect` (@blakeembrey)
+- [#1130](https://github.com/request/request/pull/1130) documentation fix: add note about npm test for contributing (@FredKSchott)
+- [#1120](https://github.com/request/request/pull/1120) Support/refactor request setup tunnel (@seanstrom)
+- [#1129](https://github.com/request/request/pull/1129) linting fix: convert double quote strings to use single quotes (@FredKSchott)
+- [#1124](https://github.com/request/request/pull/1124) linting fix: remove unneccesary semi-colons (@FredKSchott)
+
+### v2.45.0 (2014/10/06)
+- [#1128](https://github.com/request/request/pull/1128) Add test for setCookie regression (@nylen)
+- [#1127](https://github.com/request/request/pull/1127) added tests around using objects as values in a query string (@bcoe)
+- [#1103](https://github.com/request/request/pull/1103) Support/refactor request constructor (@nylen, @seanstrom)
+- [#1119](https://github.com/request/request/pull/1119) add basic linting to request library (@FredKSchott)
+- [#1121](https://github.com/request/request/pull/1121) Revert "Explicitly use sync versions of cookie functions" (@nylen)
+- [#1118](https://github.com/request/request/pull/1118) linting fix: Restructure bad empty if statement (@FredKSchott)
+- [#1117](https://github.com/request/request/pull/1117) Fix a bad check for valid URIs (@FredKSchott)
+- [#1113](https://github.com/request/request/pull/1113) linting fix: space out operators (@FredKSchott)
+- [#1116](https://github.com/request/request/pull/1116) Fix typo in `noProxyHost` definition (@FredKSchott)
+- [#1114](https://github.com/request/request/pull/1114) linting fix: Added a `new` operator that was missing when creating and throwing a new error (@FredKSchott)
+- [#1096](https://github.com/request/request/pull/1096) No_proxy support (@samcday)
+- [#1107](https://github.com/request/request/pull/1107) linting-fix: remove unused variables (@FredKSchott)
+- [#1112](https://github.com/request/request/pull/1112) linting fix: Make return values consistent and more straitforward (@FredKSchott)
+- [#1111](https://github.com/request/request/pull/1111) linting fix: authPieces was getting redeclared (@FredKSchott)
+- [#1105](https://github.com/request/request/pull/1105) Use strict mode in request (@FredKSchott)
+- [#1110](https://github.com/request/request/pull/1110) linting fix: replace lazy '==' with more strict '===' (@FredKSchott)
+- [#1109](https://github.com/request/request/pull/1109) linting fix: remove function call from if-else conditional statement (@FredKSchott)
+- [#1102](https://github.com/request/request/pull/1102) Fix to allow setting a `requester` on recursive calls to `request.defaults` (@tikotzky)
+- [#1095](https://github.com/request/request/pull/1095) Tweaking engines in package.json (@pdehaan)
+- [#1082](https://github.com/request/request/pull/1082) Forward the socket event from the httpModule request (@seanstrom)
+- [#972](https://github.com/request/request/pull/972) Clarify gzip handling in the README (@kevinoid)
+- [#1089](https://github.com/request/request/pull/1089) Mention that encoding defaults to utf8, not Buffer (@stuartpb)
+- [#1088](https://github.com/request/request/pull/1088) Fix cookie example in README.md and make it more clear (@pipi32167)
+- [#1027](https://github.com/request/request/pull/1027) Add support for multipart form data in request options. (@crocket)
+- [#1076](https://github.com/request/request/pull/1076) use Request.abort() to abort the request when the request has timed-out (@seanstrom)
+- [#1068](https://github.com/request/request/pull/1068) add optional postamble required by .NET multipart requests (@netpoetica)
+
+### v2.43.0 (2014/09/18)
+- [#1057](https://github.com/request/request/pull/1057) Defaults should not overwrite defined options (@davidwood)
+- [#1046](https://github.com/request/request/pull/1046) Propagate datastream errors, useful in case gzip fails. (@ZJONSSON, @Janpot)
+- [#1063](https://github.com/request/request/pull/1063) copy the input headers object #1060 (@finnp)
+- [#1031](https://github.com/request/request/pull/1031) Explicitly use sync versions of cookie functions (@ZJONSSON)
+- [#1056](https://github.com/request/request/pull/1056) Fix redirects when passing url.parse(x) as URL to convenience method (@nylen)
+
+### v2.42.0 (2014/09/04)
+- [#1053](https://github.com/request/request/pull/1053) Fix #1051 Parse auth properly when using non-tunneling proxy (@isaacs)
+
+### v2.41.0 (2014/09/04)
+- [#1050](https://github.com/request/request/pull/1050) Pass whitelisted headers to tunneling proxy. Organize all tunneling logic. (@isaacs, @Feldhacker)
+- [#1035](https://github.com/request/request/pull/1035) souped up nodei.co badge (@rvagg)
+- [#1048](https://github.com/request/request/pull/1048) Aws is now possible over a proxy (@steven-aerts)
+- [#1039](https://github.com/request/request/pull/1039) extract out helper functions to a helper file (@seanstrom)
+- [#1021](https://github.com/request/request/pull/1021) Support/refactor indexjs (@seanstrom)
+- [#1033](https://github.com/request/request/pull/1033) Improve and document debug options (@nylen)
+- [#1034](https://github.com/request/request/pull/1034) Fix readme headings (@nylen)
+- [#1030](https://github.com/request/request/pull/1030) Allow recursive request.defaults (@tikotzky)
+- [#1029](https://github.com/request/request/pull/1029) Fix a couple of typos (@nylen)
+- [#675](https://github.com/request/request/pull/675) Checking for SSL fault on connection before reading SSL properties (@VRMink)
+- [#989](https://github.com/request/request/pull/989) Added allowRedirect function. Should return true if redirect is allowed or false otherwise (@doronin)
+- [#1025](https://github.com/request/request/pull/1025) [fixes #1023] Set self._ended to true once response has ended (@mridgway)
+- [#1020](https://github.com/request/request/pull/1020) Add back removed debug metadata (@FredKSchott)
+- [#1008](https://github.com/request/request/pull/1008) Moving to module instead of cutomer buffer concatenation. (@mikeal)
+- [#770](https://github.com/request/request/pull/770) Added dependency badge for README file; (@timgluz)
+- [#1016](https://github.com/request/request/pull/1016) toJSON no longer results in an infinite loop, returns simple objects (@FredKSchott)
+- [#1018](https://github.com/request/request/pull/1018) Remove pre-0.4.4 HTTPS fix (@mmalecki)
+- [#1006](https://github.com/request/request/pull/1006) Migrate to caseless, fixes #1001 (@mikeal)
+- [#995](https://github.com/request/request/pull/995) Fix parsing array of objects (@sjonnet19)
+- [#999](https://github.com/request/request/pull/999) Fix fallback for browserify for optional modules. (@eiriksm)
+- [#996](https://github.com/request/request/pull/996) Wrong oauth signature when multiple same param keys exist [updated] (@bengl)
+
+### v2.40.0 (2014/08/06)
+- [#992](https://github.com/request/request/pull/992) Fix security vulnerability. Update qs (@poeticninja)
+- [#988](https://github.com/request/request/pull/988) “--” -> “—” (@upisfree)
+- [#987](https://github.com/request/request/pull/987) Show optional modules as being loaded by the module that reqeusted them (@iarna)
+
+### v2.39.0 (2014/07/24)
+- [#976](https://github.com/request/request/pull/976) Update README.md (@pvoznenko)
+
+### v2.38.0 (2014/07/22)
+- [#952](https://github.com/request/request/pull/952) Adding support to client certificate with proxy use case (@ofirshaked)
+- [#884](https://github.com/request/request/pull/884) Documented tough-cookie installation. (@wbyoung)
+- [#935](https://github.com/request/request/pull/935) Correct repository url (@fritx)
+- [#963](https://github.com/request/request/pull/963) Update changelog (@nylen)
+- [#960](https://github.com/request/request/pull/960) Support gzip with encoding on node pre-v0.9.4 (@kevinoid)
+- [#953](https://github.com/request/request/pull/953) Add async Content-Length computation when using form-data (@LoicMahieu)
+- [#844](https://github.com/request/request/pull/844) Add support for HTTP[S]_PROXY environment variables. Fixes #595. (@jvmccarthy)
+- [#946](https://github.com/request/request/pull/946) defaults: merge headers (@aj0strow)
+
+### v2.37.0 (2014/07/07)
+- [#957](https://github.com/request/request/pull/957) Silence EventEmitter memory leak warning #311 (@watson)
+- [#955](https://github.com/request/request/pull/955) check for content-length header before setting it in nextTick (@camilleanne)
+- [#951](https://github.com/request/request/pull/951) Add support for gzip content decoding (@kevinoid)
+- [#949](https://github.com/request/request/pull/949) Manually enter querystring in form option (@charlespwd)
+- [#944](https://github.com/request/request/pull/944) Make request work with browserify (@eiriksm)
+- [#943](https://github.com/request/request/pull/943) New mime module (@eiriksm)
+- [#927](https://github.com/request/request/pull/927) Bump version of hawk dep. (@samccone)
+- [#907](https://github.com/request/request/pull/907) append secureOptions to poolKey (@medovob)
+
+### v2.35.0 (2014/05/17)
+- [#901](https://github.com/request/request/pull/901) Fixes #555 (@pigulla)
+- [#897](https://github.com/request/request/pull/897) merge with default options (@vohof)
+- [#891](https://github.com/request/request/pull/891) fixes 857 - options object is mutated by calling request (@lalitkapoor)
+- [#869](https://github.com/request/request/pull/869) Pipefilter test (@tgohn)
+- [#866](https://github.com/request/request/pull/866) Fix typo (@dandv)
+- [#861](https://github.com/request/request/pull/861) Add support for RFC 6750 Bearer Tokens (@phedny)
+- [#809](https://github.com/request/request/pull/809) upgrade tunnel-proxy to 0.4.0 (@ksato9700)
+- [#850](https://github.com/request/request/pull/850) Fix word consistency in readme (@0xNobody)
+- [#810](https://github.com/request/request/pull/810) add some exposition to mpu example in README.md (@mikermcneil)
+- [#840](https://github.com/request/request/pull/840) improve error reporting for invalid protocols (@FND)
+- [#821](https://github.com/request/request/pull/821) added secureOptions back (@nw)
+- [#815](https://github.com/request/request/pull/815) Create changelog based on pull requests (@lalitkapoor)
+
+### v2.34.0 (2014/02/18)
+- [#516](https://github.com/request/request/pull/516) UNIX Socket URL Support (@lyuzashi)
+- [#801](https://github.com/request/request/pull/801) 794 ignore cookie parsing and domain errors (@lalitkapoor)
+- [#802](https://github.com/request/request/pull/802) Added the Apache license to the package.json. (@keskival)
+- [#793](https://github.com/request/request/pull/793) Adds content-length calculation when submitting forms using form-data li... (@Juul)
+- [#785](https://github.com/request/request/pull/785) Provide ability to override content-type when `json` option used (@vvo)
+- [#781](https://github.com/request/request/pull/781) simpler isReadStream function (@joaojeronimo)
+
+### v2.32.0 (2014/01/16)
+- [#767](https://github.com/request/request/pull/767) Use tough-cookie CookieJar sync API (@stash)
+- [#764](https://github.com/request/request/pull/764) Case-insensitive authentication scheme (@bobyrizov)
+- [#763](https://github.com/request/request/pull/763) Upgrade tough-cookie to 0.10.0 (@stash)
+- [#744](https://github.com/request/request/pull/744) Use Cookie.parse (@lalitkapoor)
+- [#757](https://github.com/request/request/pull/757) require aws-sign2 (@mafintosh)
+
+### v2.31.0 (2014/01/08)
+- [#645](https://github.com/request/request/pull/645) update twitter api url to v1.1 (@mick)
+- [#746](https://github.com/request/request/pull/746) README: Markdown code highlight (@weakish)
+- [#745](https://github.com/request/request/pull/745) updating setCookie example to make it clear that the callback is required (@emkay)
+- [#742](https://github.com/request/request/pull/742) Add note about JSON output body type (@iansltx)
+- [#741](https://github.com/request/request/pull/741) README example is using old cookie jar api (@emkay)
+- [#736](https://github.com/request/request/pull/736) Fix callback arguments documentation (@mmalecki)
+
+### v2.30.0 (2013/12/13)
+- [#732](https://github.com/request/request/pull/732) JSHINT: Creating global 'for' variable. Should be 'for (var ...'. (@Fritz-Lium)
+- [#730](https://github.com/request/request/pull/730) better HTTP DIGEST support (@dai-shi)
+- [#728](https://github.com/request/request/pull/728) Fix TypeError when calling request.cookie (@scarletmeow)
+
+### v2.29.0 (2013/12/06)
+- [#727](https://github.com/request/request/pull/727) fix requester bug (@jchris)
+
+### v2.28.0 (2013/12/04)
+- [#724](https://github.com/request/request/pull/724) README.md: add custom HTTP Headers example. (@tcort)
+- [#719](https://github.com/request/request/pull/719) Made a comment gender neutral. (@oztu)
+- [#715](https://github.com/request/request/pull/715) Request.multipart no longer crashes when header 'Content-type' present (@pastaclub)
+- [#710](https://github.com/request/request/pull/710) Fixing listing in callback part of docs. (@lukasz-zak)
+- [#696](https://github.com/request/request/pull/696) Edited README.md for formatting and clarity of phrasing (@Zearin)
+- [#694](https://github.com/request/request/pull/694) Typo in README (@VRMink)
+- [#690](https://github.com/request/request/pull/690) Handle blank password in basic auth. (@diversario)
+- [#682](https://github.com/request/request/pull/682) Optional dependencies (@Turbo87)
+- [#683](https://github.com/request/request/pull/683) Travis CI support (@Turbo87)
+- [#674](https://github.com/request/request/pull/674) change cookie module,to tough-cookie.please check it . (@sxyizhiren)
+- [#666](https://github.com/request/request/pull/666) make `ciphers` and `secureProtocol` to work in https request (@richarddong)
+- [#656](https://github.com/request/request/pull/656) Test case for #304. (@diversario)
+- [#662](https://github.com/request/request/pull/662) option.tunnel to explicitly disable tunneling (@seanmonstar)
+- [#659](https://github.com/request/request/pull/659) fix failure when running with NODE_DEBUG=request, and a test for that (@jrgm)
+- [#630](https://github.com/request/request/pull/630) Send random cnonce for HTTP Digest requests (@wprl)
+
+### v2.27.0 (2013/08/15)
+- [#619](https://github.com/request/request/pull/619) decouple things a bit (@joaojeronimo)
+
+### v2.26.0 (2013/08/07)
+- [#613](https://github.com/request/request/pull/613) Fixes #583, moved initialization of self.uri.pathname (@lexander)
+- [#605](https://github.com/request/request/pull/605) Only include ":" + pass in Basic Auth if it's defined (fixes #602) (@bendrucker)
+
+### v2.24.0 (2013/07/23)
+- [#596](https://github.com/request/request/pull/596) Global agent is being used when pool is specified (@Cauldrath)
+- [#594](https://github.com/request/request/pull/594) Emit complete event when there is no callback (@RomainLK)
+- [#601](https://github.com/request/request/pull/601) Fixed a small typo (@michalstanko)
+
+### v2.23.0 (2013/07/23)
+- [#589](https://github.com/request/request/pull/589) Prevent setting headers after they are sent (@geek)
+- [#587](https://github.com/request/request/pull/587) Global cookie jar disabled by default (@threepointone)
+
+### v2.22.0 (2013/07/05)
+- [#544](https://github.com/request/request/pull/544) Update http-signature version. (@davidlehn)
+- [#581](https://github.com/request/request/pull/581) Fix spelling of "ignoring." (@bigeasy)
+- [#568](https://github.com/request/request/pull/568) use agentOptions to create agent when specified in request (@SamPlacette)
+- [#564](https://github.com/request/request/pull/564) Fix redirections (@criloz)
+- [#541](https://github.com/request/request/pull/541) The exported request function doesn't have an auth method (@tschaub)
+- [#542](https://github.com/request/request/pull/542) Expose Request class (@regality)
+
+### v2.21.0 (2013/04/30)
+- [#536](https://github.com/request/request/pull/536) Allow explicitly empty user field for basic authentication. (@mikeando)
+- [#532](https://github.com/request/request/pull/532) fix typo (@fredericosilva)
+- [#497](https://github.com/request/request/pull/497) Added redirect event (@Cauldrath)
+- [#503](https://github.com/request/request/pull/503) Fix basic auth for passwords that contain colons (@tonistiigi)
+- [#521](https://github.com/request/request/pull/521) Improving test-localAddress.js (@noway421)
+- [#529](https://github.com/request/request/pull/529) dependencies versions bump (@jodaka)
+
+### v2.17.0 (2013/04/22)
+- [#523](https://github.com/request/request/pull/523) Updating dependencies (@noway421)
+- [#520](https://github.com/request/request/pull/520) Fixing test-tunnel.js (@noway421)
+- [#519](https://github.com/request/request/pull/519) Update internal path state on post-creation QS changes (@jblebrun)
+- [#510](https://github.com/request/request/pull/510) Add HTTP Signature support. (@davidlehn)
+- [#502](https://github.com/request/request/pull/502) Fix POST (and probably other) requests that are retried after 401 Unauthorized (@nylen)
+- [#508](https://github.com/request/request/pull/508) Honor the .strictSSL option when using proxies (tunnel-agent) (@jhs)
+- [#512](https://github.com/request/request/pull/512) Make password optional to support the format: http://username@hostname/ (@pajato1)
+- [#513](https://github.com/request/request/pull/513) add 'localAddress' support (@yyfrankyy)
+- [#498](https://github.com/request/request/pull/498) Moving response emit above setHeaders on destination streams (@kenperkins)
+- [#490](https://github.com/request/request/pull/490) Empty response body (3-rd argument) must be passed to callback as an empty string (@Olegas)
+- [#479](https://github.com/request/request/pull/479) Changing so if Accept header is explicitly set, sending json does not ov... (@RoryH)
+- [#475](https://github.com/request/request/pull/475) Use `unescape` from `querystring` (@shimaore)
+- [#473](https://github.com/request/request/pull/473) V0.10 compat (@isaacs)
+- [#471](https://github.com/request/request/pull/471) Using querystring library from visionmedia (@kbackowski)
+- [#461](https://github.com/request/request/pull/461) Strip the UTF8 BOM from a UTF encoded response (@kppullin)
+- [#460](https://github.com/request/request/pull/460) hawk 0.10.0 (@hueniverse)
+- [#462](https://github.com/request/request/pull/462) if query params are empty, then request path shouldn't end with a '?' (merges cleanly now) (@jaipandya)
+- [#456](https://github.com/request/request/pull/456) hawk 0.9.0 (@hueniverse)
+- [#429](https://github.com/request/request/pull/429) Copy options before adding callback. (@nrn)
+- [#454](https://github.com/request/request/pull/454) Destroy the response if present when destroying the request (clean merge) (@mafintosh)
+- [#310](https://github.com/request/request/pull/310) Twitter Oauth Stuff Out of Date; Now Updated (@joemccann)
+- [#413](https://github.com/request/request/pull/413) rename googledoodle.png to .jpg (@nfriedly)
+- [#448](https://github.com/request/request/pull/448) Convenience method for PATCH (@mloar)
+- [#444](https://github.com/request/request/pull/444) protect against double callbacks on error path (@spollack)
+- [#433](https://github.com/request/request/pull/433) Added support for HTTPS cert & key (@mmalecki)
+- [#430](https://github.com/request/request/pull/430) Respect specified {Host,host} headers, not just {host} (@andrewschaaf)
+- [#415](https://github.com/request/request/pull/415) Fixed a typo. (@jerem)
+- [#338](https://github.com/request/request/pull/338) Add more auth options, including digest support (@nylen)
+- [#403](https://github.com/request/request/pull/403) Optimize environment lookup to happen once only (@mmalecki)
+- [#398](https://github.com/request/request/pull/398) Add more reporting to tests (@mmalecki)
+- [#388](https://github.com/request/request/pull/388) Ensure "safe" toJSON doesn't break EventEmitters (@othiym23)
+- [#381](https://github.com/request/request/pull/381) Resolving "Invalid signature. Expected signature base string: " (@landeiro)
+- [#380](https://github.com/request/request/pull/380) Fixes missing host header on retried request when using forever agent (@mac-)
+- [#376](https://github.com/request/request/pull/376) Headers lost on redirect (@kapetan)
+- [#375](https://github.com/request/request/pull/375) Fix for missing oauth_timestamp parameter (@jplock)
+- [#374](https://github.com/request/request/pull/374) Correct Host header for proxy tunnel CONNECT (@youurayy)
+- [#370](https://github.com/request/request/pull/370) Twitter reverse auth uses x_auth_mode not x_auth_type (@drudge)
+- [#369](https://github.com/request/request/pull/369) Don't remove x_auth_mode for Twitter reverse auth (@drudge)
+- [#344](https://github.com/request/request/pull/344) Make AWS auth signing find headers correctly (@nlf)
+- [#363](https://github.com/request/request/pull/363) rfc3986 on base_uri, now passes tests (@jeffmarshall)
+- [#362](https://github.com/request/request/pull/362) Running `rfc3986` on `base_uri` in `oauth.hmacsign` instead of just `encodeURIComponent` (@jeffmarshall)
+- [#361](https://github.com/request/request/pull/361) Don't create a Content-Length header if we already have it set (@danjenkins)
+- [#360](https://github.com/request/request/pull/360) Delete self._form along with everything else on redirect (@jgautier)
+- [#355](https://github.com/request/request/pull/355) stop sending erroneous headers on redirected requests (@azylman)
+- [#332](https://github.com/request/request/pull/332) Fix #296 - Only set Content-Type if body exists (@Marsup)
+- [#343](https://github.com/request/request/pull/343) Allow AWS to work in more situations, added a note in the README on its usage (@nlf)
+- [#320](https://github.com/request/request/pull/320) request.defaults() doesn't need to wrap jar() (@StuartHarris)
+- [#322](https://github.com/request/request/pull/322) Fix + test for piped into request bumped into redirect. #321 (@alexindigo)
+- [#326](https://github.com/request/request/pull/326) Do not try to remove listener from an undefined connection (@strk)
+- [#318](https://github.com/request/request/pull/318) Pass servername to tunneling secure socket creation (@isaacs)
+- [#317](https://github.com/request/request/pull/317) Workaround for #313 (@isaacs)
+- [#293](https://github.com/request/request/pull/293) Allow parser errors to bubble up to request (@mscdex)
+- [#290](https://github.com/request/request/pull/290) A test for #289 (@isaacs)
+- [#280](https://github.com/request/request/pull/280) Like in node.js print options if NODE_DEBUG contains the word request (@Filirom1)
+- [#207](https://github.com/request/request/pull/207) Fix #206 Change HTTP/HTTPS agent when redirecting between protocols (@isaacs)
+- [#214](https://github.com/request/request/pull/214) documenting additional behavior of json option (@jphaas)
+- [#272](https://github.com/request/request/pull/272) Boundary begins with CRLF? (@elspoono)
+- [#284](https://github.com/request/request/pull/284) Remove stray `console.log()` call in multipart generator. (@bcherry)
+- [#241](https://github.com/request/request/pull/241) Composability updates suggested by issue #239 (@polotek)
+- [#282](https://github.com/request/request/pull/282) OAuth Authorization header contains non-"oauth_" parameters (@jplock)
+- [#279](https://github.com/request/request/pull/279) fix tests with boundary by injecting boundry from header (@benatkin)
+- [#273](https://github.com/request/request/pull/273) Pipe back pressure issue (@mafintosh)
+- [#268](https://github.com/request/request/pull/268) I'm not OCD seriously (@TehShrike)
+- [#263](https://github.com/request/request/pull/263) Bug in OAuth key generation for sha1 (@nanodocumet)
+- [#265](https://github.com/request/request/pull/265) uncaughtException when redirected to invalid URI (@naholyr)
+- [#262](https://github.com/request/request/pull/262) JSON test should check for equality (@timshadel)
+- [#261](https://github.com/request/request/pull/261) Setting 'pool' to 'false' does NOT disable Agent pooling (@timshadel)
+- [#249](https://github.com/request/request/pull/249) Fix for the fix of your (closed) issue #89 where self.headers[content-length] is set to 0 for all methods (@sethbridges)
+- [#255](https://github.com/request/request/pull/255) multipart allow body === '' ( the empty string ) (@Filirom1)
+- [#260](https://github.com/request/request/pull/260) fixed just another leak of 'i' (@sreuter)
+- [#246](https://github.com/request/request/pull/246) Fixing the set-cookie header (@jeromegn)
+- [#243](https://github.com/request/request/pull/243) Dynamic boundary (@zephrax)
+- [#240](https://github.com/request/request/pull/240) don't error when null is passed for options (@polotek)
+- [#211](https://github.com/request/request/pull/211) Replace all occurrences of special chars in RFC3986 (@chriso)
+- [#224](https://github.com/request/request/pull/224) Multipart content-type change (@janjongboom)
+- [#217](https://github.com/request/request/pull/217) need to use Authorization (titlecase) header with Tumblr OAuth (@visnup)
+- [#203](https://github.com/request/request/pull/203) Fix cookie and redirect bugs and add auth support for HTTPS tunnel (@milewise)
+- [#199](https://github.com/request/request/pull/199) Tunnel (@isaacs)
+- [#198](https://github.com/request/request/pull/198) Bugfix on forever usage of util.inherits (@isaacs)
+- [#197](https://github.com/request/request/pull/197) Make ForeverAgent work with HTTPS (@isaacs)
+- [#193](https://github.com/request/request/pull/193) Fixes GH-119 (@goatslacker)
+- [#188](https://github.com/request/request/pull/188) Add abort support to the returned request (@itay)
+- [#176](https://github.com/request/request/pull/176) Querystring option (@csainty)
+- [#182](https://github.com/request/request/pull/182) Fix request.defaults to support (uri, options, callback) api (@twilson63)
+- [#180](https://github.com/request/request/pull/180) Modified the post, put, head and del shortcuts to support uri optional param (@twilson63)
+- [#179](https://github.com/request/request/pull/179) fix to add opts in .pipe(stream, opts) (@substack)
+- [#177](https://github.com/request/request/pull/177) Issue #173 Support uri as first and optional config as second argument (@twilson63)
+- [#170](https://github.com/request/request/pull/170) can't create a cookie in a wrapped request (defaults) (@fabianonunes)
+- [#168](https://github.com/request/request/pull/168) Picking off an EasyFix by adding some missing mimetypes. (@serby)
+- [#161](https://github.com/request/request/pull/161) Fix cookie jar/headers.cookie collision (#125) (@papandreou)
+- [#162](https://github.com/request/request/pull/162) Fix issue #159 (@dpetukhov)
+- [#90](https://github.com/request/request/pull/90) add option followAllRedirects to follow post/put redirects (@jroes)
+- [#148](https://github.com/request/request/pull/148) Retry Agent (@thejh)
+- [#146](https://github.com/request/request/pull/146) Multipart should respect content-type if previously set (@apeace)
+- [#144](https://github.com/request/request/pull/144) added "form" option to readme (@petejkim)
+- [#133](https://github.com/request/request/pull/133) Fixed cookies parsing (@afanasy)
+- [#135](https://github.com/request/request/pull/135) host vs hostname (@iangreenleaf)
+- [#132](https://github.com/request/request/pull/132) return the body as a Buffer when encoding is set to null (@jahewson)
+- [#112](https://github.com/request/request/pull/112) Support using a custom http-like module (@jhs)
+- [#104](https://github.com/request/request/pull/104) Cookie handling contains bugs (@janjongboom)
+- [#121](https://github.com/request/request/pull/121) Another patch for cookie handling regression (@jhurliman)
+- [#117](https://github.com/request/request/pull/117) Remove the global `i` (@3rd-Eden)
+- [#110](https://github.com/request/request/pull/110) Update to Iris Couch URL (@jhs)
+- [#86](https://github.com/request/request/pull/86) Can't post binary to multipart requests (@developmentseed)
+- [#105](https://github.com/request/request/pull/105) added test for proxy option. (@dominictarr)
+- [#102](https://github.com/request/request/pull/102) Implemented cookies - closes issue 82: https://github.com/mikeal/request/issues/82 (@alessioalex)
+- [#97](https://github.com/request/request/pull/97) Typo in previous pull causes TypeError in non-0.5.11 versions (@isaacs)
+- [#96](https://github.com/request/request/pull/96) Authless parsed url host support (@isaacs)
+- [#81](https://github.com/request/request/pull/81) Enhance redirect handling (@danmactough)
+- [#78](https://github.com/request/request/pull/78) Don't try to do strictSSL for non-ssl connections (@isaacs)
+- [#76](https://github.com/request/request/pull/76) Bug when a request fails and a timeout is set (@Marsup)
+- [#70](https://github.com/request/request/pull/70) add test script to package.json (@isaacs)
+- [#73](https://github.com/request/request/pull/73) Fix #71 Respect the strictSSL flag (@isaacs)
+- [#69](https://github.com/request/request/pull/69) Flatten chunked requests properly (@isaacs)
+- [#67](https://github.com/request/request/pull/67) fixed global variable leaks (@aheckmann)
+- [#66](https://github.com/request/request/pull/66) Do not overwrite established content-type headers for read stream deliver (@voodootikigod)
+- [#53](https://github.com/request/request/pull/53) Parse json: Issue #51 (@benatkin)
+- [#45](https://github.com/request/request/pull/45) Added timeout option (@mbrevoort)
+- [#35](https://github.com/request/request/pull/35) The "end" event isn't emitted for some responses (@voxpelli)
+- [#31](https://github.com/request/request/pull/31) Error on piping a request to a destination (@tobowers)
\ No newline at end of file
--- /dev/null
+# This is an OPEN Open Source Project
+
+-----------------------------------------
+
+## What?
+
+Individuals making significant and valuable contributions are given
+commit-access to the project to contribute as they see fit. This project is
+more like an open wiki than a standard guarded open source project.
+
+## Rules
+
+There are a few basic ground-rules for contributors:
+
+1. **No `--force` pushes** or modifying the Git history in any way.
+1. **Non-master branches** ought to be used for ongoing work.
+1. **External API changes and significant modifications** ought to be subject
+ to an **internal pull-request** to solicit feedback from other contributors.
+1. Internal pull-requests to solicit feedback are *encouraged* for any other
+ non-trivial contribution but left to the discretion of the contributor.
+1. For significant changes wait a full 24 hours before merging so that active
+ contributors who are distributed throughout the world have a chance to weigh
+ in.
+1. Contributors should attempt to adhere to the prevailing code-style.
+1. Run `npm test` locally before submitting your PR, to catch any easy to miss
+ style & testing issues. To diagnose test failures, there are two ways to
+ run a single test file:
+ - `node_modules/.bin/taper tests/test-file.js` - run using the default
+ [`taper`](https://github.com/nylen/taper) test reporter.
+ - `node tests/test-file.js` - view the raw
+ [tap](https://testanything.org/) output.
+
+
+## Releases
+
+Declaring formal releases remains the prerogative of the project maintainer.
+
+## Changes to this arrangement
+
+This is an experiment and feedback is welcome! This document may also be
+subject to pull-requests or changes by contributors where you believe you have
+something valuable to add or change.
+
+-----------------------------------------
--- /dev/null
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
--- /dev/null
+
+# Request - Simplified HTTP client
+
+[](https://nodei.co/npm/request/)
+
+[](https://travis-ci.org/request/request)
+[](https://coveralls.io/r/request/request)
+[](https://gitter.im/request/request?utm_source=badge)
+
+
+## Super simple to use
+
+Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.
+
+```js
+var request = require('request');
+request('http://www.google.com', function (error, response, body) {
+ if (!error && response.statusCode == 200) {
+ console.log(body) // Show the HTML for the Google homepage.
+ }
+})
+```
+
+
+## Table of contents
+
+- [Streaming](#streaming)
+- [Forms](#forms)
+- [HTTP Authentication](#http-authentication)
+- [Custom HTTP Headers](#custom-http-headers)
+- [OAuth Signing](#oauth-signing)
+- [Proxies](#proxies)
+- [Unix Domain Sockets](#unix-domain-sockets)
+- [TLS/SSL Protocol](#tlsssl-protocol)
+- [Support for HAR 1.2](#support-for-har-12)
+- [**All Available Options**](#requestoptions-callback)
+
+Request also offers [convenience methods](#convenience-methods) like
+`request.defaults` and `request.post`, and there are
+lots of [usage examples](#examples) and several
+[debugging techniques](#debugging).
+
+
+---
+
+
+## Streaming
+
+You can stream any response to a file stream.
+
+```js
+request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))
+```
+
+You can also stream a file to a PUT or POST request. This method will also check the file extension against a mapping of file extensions to content-types (in this case `application/json`) and use the proper `content-type` in the PUT request (if the headers don’t already provide one).
+
+```js
+fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))
+```
+
+Request can also `pipe` to itself. When doing so, `content-type` and `content-length` are preserved in the PUT headers.
+
+```js
+request.get('http://google.com/img.png').pipe(request.put('http://mysite.com/img.png'))
+```
+
+Request emits a "response" event when a response is received. The `response` argument will be an instance of [http.IncomingMessage](http://nodejs.org/api/http.html#http_http_incomingmessage).
+
+```js
+request
+ .get('http://google.com/img.png')
+ .on('response', function(response) {
+ console.log(response.statusCode) // 200
+ console.log(response.headers['content-type']) // 'image/png'
+ })
+ .pipe(request.put('http://mysite.com/img.png'))
+```
+
+To easily handle errors when streaming requests, listen to the `error` event before piping:
+
+```js
+request
+ .get('http://mysite.com/doodle.png')
+ .on('error', function(err) {
+ console.log(err)
+ })
+ .pipe(fs.createWriteStream('doodle.png'))
+```
+
+Now let’s get fancy.
+
+```js
+http.createServer(function (req, resp) {
+ if (req.url === '/doodle.png') {
+ if (req.method === 'PUT') {
+ req.pipe(request.put('http://mysite.com/doodle.png'))
+ } else if (req.method === 'GET' || req.method === 'HEAD') {
+ request.get('http://mysite.com/doodle.png').pipe(resp)
+ }
+ }
+})
+```
+
+You can also `pipe()` from `http.ServerRequest` instances, as well as to `http.ServerResponse` instances. The HTTP method, headers, and entity-body data will be sent. Which means that, if you don't really care about security, you can do:
+
+```js
+http.createServer(function (req, resp) {
+ if (req.url === '/doodle.png') {
+ var x = request('http://mysite.com/doodle.png')
+ req.pipe(x)
+ x.pipe(resp)
+ }
+})
+```
+
+And since `pipe()` returns the destination stream in ≥ Node 0.5.x you can do one line proxying. :)
+
+```js
+req.pipe(request('http://mysite.com/doodle.png')).pipe(resp)
+```
+
+Also, none of this new functionality conflicts with requests previous features, it just expands them.
+
+```js
+var r = request.defaults({'proxy':'http://localproxy.com'})
+
+http.createServer(function (req, resp) {
+ if (req.url === '/doodle.png') {
+ r.get('http://google.com/doodle.png').pipe(resp)
+ }
+})
+```
+
+You can still use intermediate proxies, the requests will still follow HTTP forwards, etc.
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## Forms
+
+`request` supports `application/x-www-form-urlencoded` and `multipart/form-data` form uploads. For `multipart/related` refer to the `multipart` API.
+
+
+#### application/x-www-form-urlencoded (URL-Encoded Forms)
+
+URL-encoded forms are simple.
+
+```js
+request.post('http://service.com/upload', {form:{key:'value'}})
+// or
+request.post('http://service.com/upload').form({key:'value'})
+// or
+request.post({url:'http://service.com/upload', form: {key:'value'}}, function(err,httpResponse,body){ /* ... */ })
+```
+
+
+#### multipart/form-data (Multipart Form Uploads)
+
+For `multipart/form-data` we use the [form-data](https://github.com/felixge/node-form-data) library by [@felixge](https://github.com/felixge). For the most cases, you can pass your upload form data via the `formData` option.
+
+
+```js
+var formData = {
+ // Pass a simple key-value pair
+ my_field: 'my_value',
+ // Pass data via Buffers
+ my_buffer: new Buffer([1, 2, 3]),
+ // Pass data via Streams
+ my_file: fs.createReadStream(__dirname + '/unicycle.jpg'),
+ // Pass multiple values /w an Array
+ attachments: [
+ fs.createReadStream(__dirname + '/attachment1.jpg'),
+ fs.createReadStream(__dirname + '/attachment2.jpg')
+ ],
+ // Pass optional meta-data with an 'options' object with style: {value: DATA, options: OPTIONS}
+ // Use case: for some types of streams, you'll need to provide "file"-related information manually.
+ // See the `form-data` README for more information about options: https://github.com/felixge/node-form-data
+ custom_file: {
+ value: fs.createReadStream('/dev/urandom'),
+ options: {
+ filename: 'topsecret.jpg',
+ contentType: 'image/jpg'
+ }
+ }
+};
+request.post({url:'http://service.com/upload', formData: formData}, function optionalCallback(err, httpResponse, body) {
+ if (err) {
+ return console.error('upload failed:', err);
+ }
+ console.log('Upload successful! Server responded with:', body);
+});
+```
+
+For advanced cases, you can access the form-data object itself via `r.form()`. This can be modified until the request is fired on the next cycle of the event-loop. (Note that this calling `form()` will clear the currently set form data for that request.)
+
+```js
+// NOTE: Advanced use-case, for normal use see 'formData' usage above
+var r = request.post('http://service.com/upload', function optionalCallback(err, httpResponse, body) { // ...
+
+var form = r.form();
+form.append('my_field', 'my_value');
+form.append('my_buffer', new Buffer([1, 2, 3]));
+form.append('custom_file', fs.createReadStream(__dirname + '/unicycle.jpg'), {filename: 'unicycle.jpg'});
+```
+See the [form-data README](https://github.com/felixge/node-form-data) for more information & examples.
+
+
+#### multipart/related
+
+Some variations in different HTTP implementations require a newline/CRLF before, after, or both before and after the boundary of a `multipart/related` request (using the multipart option). This has been observed in the .NET WebAPI version 4.0. You can turn on a boundary preambleCRLF or postamble by passing them as `true` to your request options.
+
+```js
+ request({
+ method: 'PUT',
+ preambleCRLF: true,
+ postambleCRLF: true,
+ uri: 'http://service.com/upload',
+ multipart: [
+ {
+ 'content-type': 'application/json'
+ body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})
+ },
+ { body: 'I am an attachment' },
+ { body: fs.createReadStream('image.png') }
+ ],
+ // alternatively pass an object containing additional options
+ multipart: {
+ chunked: false,
+ data: [
+ {
+ 'content-type': 'application/json',
+ body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})
+ },
+ { body: 'I am an attachment' }
+ ]
+ }
+ },
+ function (error, response, body) {
+ if (error) {
+ return console.error('upload failed:', error);
+ }
+ console.log('Upload successful! Server responded with:', body);
+ })
+```
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## HTTP Authentication
+
+```js
+request.get('http://some.server.com/').auth('username', 'password', false);
+// or
+request.get('http://some.server.com/', {
+ 'auth': {
+ 'user': 'username',
+ 'pass': 'password',
+ 'sendImmediately': false
+ }
+});
+// or
+request.get('http://some.server.com/').auth(null, null, true, 'bearerToken');
+// or
+request.get('http://some.server.com/', {
+ 'auth': {
+ 'bearer': 'bearerToken'
+ }
+});
+```
+
+If passed as an option, `auth` should be a hash containing values:
+
+- `user` || `username`
+- `pass` || `password`
+- `sendImmediately` (optional)
+- `bearer` (optional)
+
+The method form takes parameters
+`auth(username, password, sendImmediately, bearer)`.
+
+`sendImmediately` defaults to `true`, which causes a basic or bearer
+authentication header to be sent. If `sendImmediately` is `false`, then
+`request` will retry with a proper authentication header after receiving a
+`401` response from the server (which must contain a `WWW-Authenticate` header
+indicating the required authentication method).
+
+Note that you can also specify basic authentication using the URL itself, as
+detailed in [RFC 1738](http://www.ietf.org/rfc/rfc1738.txt). Simply pass the
+`user:password` before the host with an `@` sign:
+
+```js
+var username = 'username',
+ password = 'password',
+ url = 'http://' + username + ':' + password + '@some.server.com';
+
+request({url: url}, function (error, response, body) {
+ // Do more stuff with 'body' here
+});
+```
+
+Digest authentication is supported, but it only works with `sendImmediately`
+set to `false`; otherwise `request` will send basic authentication on the
+initial request, which will probably cause the request to fail.
+
+Bearer authentication is supported, and is activated when the `bearer` value is
+available. The value may be either a `String` or a `Function` returning a
+`String`. Using a function to supply the bearer token is particularly useful if
+used in conjuction with `defaults` to allow a single function to supply the
+last known token at the time of sending a request, or to compute one on the fly.
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## Custom HTTP Headers
+
+HTTP Headers, such as `User-Agent`, can be set in the `options` object.
+In the example below, we call the github API to find out the number
+of stars and forks for the request repository. This requires a
+custom `User-Agent` header as well as https.
+
+```js
+var request = require('request');
+
+var options = {
+ url: 'https://api.github.com/repos/request/request',
+ headers: {
+ 'User-Agent': 'request'
+ }
+};
+
+function callback(error, response, body) {
+ if (!error && response.statusCode == 200) {
+ var info = JSON.parse(body);
+ console.log(info.stargazers_count + " Stars");
+ console.log(info.forks_count + " Forks");
+ }
+}
+
+request(options, callback);
+```
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## OAuth Signing
+
+[OAuth version 1.0](https://tools.ietf.org/html/rfc5849) is supported. The
+default signing algorithm is
+[HMAC-SHA1](https://tools.ietf.org/html/rfc5849#section-3.4.2):
+
+```js
+// OAuth1.0 - 3-legged server side flow (Twitter example)
+// step 1
+var qs = require('querystring')
+ , oauth =
+ { callback: 'http://mysite.com/callback/'
+ , consumer_key: CONSUMER_KEY
+ , consumer_secret: CONSUMER_SECRET
+ }
+ , url = 'https://api.twitter.com/oauth/request_token'
+ ;
+request.post({url:url, oauth:oauth}, function (e, r, body) {
+ // Ideally, you would take the body in the response
+ // and construct a URL that a user clicks on (like a sign in button).
+ // The verifier is only available in the response after a user has
+ // verified with twitter that they are authorizing your app.
+
+ // step 2
+ var req_data = qs.parse(body)
+ var uri = 'https://api.twitter.com/oauth/authenticate'
+ + '?' + qs.stringify({oauth_token: req_data.oauth_token})
+ // redirect the user to the authorize uri
+
+ // step 3
+ // after the user is redirected back to your server
+ var auth_data = qs.parse(body)
+ , oauth =
+ { consumer_key: CONSUMER_KEY
+ , consumer_secret: CONSUMER_SECRET
+ , token: auth_data.oauth_token
+ , token_secret: req_data.oauth_token_secret
+ , verifier: auth_data.oauth_verifier
+ }
+ , url = 'https://api.twitter.com/oauth/access_token'
+ ;
+ request.post({url:url, oauth:oauth}, function (e, r, body) {
+ // ready to make signed requests on behalf of the user
+ var perm_data = qs.parse(body)
+ , oauth =
+ { consumer_key: CONSUMER_KEY
+ , consumer_secret: CONSUMER_SECRET
+ , token: perm_data.oauth_token
+ , token_secret: perm_data.oauth_token_secret
+ }
+ , url = 'https://api.twitter.com/1.1/users/show.json'
+ , qs =
+ { screen_name: perm_data.screen_name
+ , user_id: perm_data.user_id
+ }
+ ;
+ request.get({url:url, oauth:oauth, json:true}, function (e, r, user) {
+ console.log(user)
+ })
+ })
+})
+```
+
+For [RSA-SHA1 signing](https://tools.ietf.org/html/rfc5849#section-3.4.3), make
+the following changes to the OAuth options object:
+* Pass `signature_method : 'RSA-SHA1'`
+* Instead of `consumer_secret`, specify a `private_key` string in
+ [PEM format](http://how2ssl.com/articles/working_with_pem_files/)
+
+For [PLAINTEXT signing](http://oauth.net/core/1.0/#anchor22), make
+the following changes to the OAuth options object:
+* Pass `signature_method : 'PLAINTEXT'`
+
+To send OAuth parameters via query params or in a post body as described in The
+[Consumer Request Parameters](http://oauth.net/core/1.0/#consumer_req_param)
+section of the oauth1 spec:
+* Pass `transport_method : 'query'` or `transport_method : 'body'` in the OAuth
+ options object.
+* `transport_method` defaults to `'header'`
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## Proxies
+
+If you specify a `proxy` option, then the request (and any subsequent
+redirects) will be sent via a connection to the proxy server.
+
+If your endpoint is an `https` url, and you are using a proxy, then
+request will send a `CONNECT` request to the proxy server *first*, and
+then use the supplied connection to connect to the endpoint.
+
+That is, first it will make a request like:
+
+```
+HTTP/1.1 CONNECT endpoint-server.com:80
+Host: proxy-server.com
+User-Agent: whatever user agent you specify
+```
+
+and then the proxy server make a TCP connection to `endpoint-server`
+on port `80`, and return a response that looks like:
+
+```
+HTTP/1.1 200 OK
+```
+
+At this point, the connection is left open, and the client is
+communicating directly with the `endpoint-server.com` machine.
+
+See [the wikipedia page on HTTP Tunneling](http://en.wikipedia.org/wiki/HTTP_tunnel)
+for more information.
+
+By default, when proxying `http` traffic, request will simply make a
+standard proxied `http` request. This is done by making the `url`
+section of the initial line of the request a fully qualified url to
+the endpoint.
+
+For example, it will make a single request that looks like:
+
+```
+HTTP/1.1 GET http://endpoint-server.com/some-url
+Host: proxy-server.com
+Other-Headers: all go here
+
+request body or whatever
+```
+
+Because a pure "http over http" tunnel offers no additional security
+or other features, it is generally simpler to go with a
+straightforward HTTP proxy in this case. However, if you would like
+to force a tunneling proxy, you may set the `tunnel` option to `true`.
+
+You can also make a standard proxied `http` request by explicitly setting
+`tunnel : false`, but **note that this will allow the proxy to see the traffic
+to/from the destination server**.
+
+If you are using a tunneling proxy, you may set the
+`proxyHeaderWhiteList` to share certain headers with the proxy.
+
+You can also set the `proxyHeaderExclusiveList` to share certain
+headers only with the proxy and not with destination host.
+
+By default, this set is:
+
+```
+accept
+accept-charset
+accept-encoding
+accept-language
+accept-ranges
+cache-control
+content-encoding
+content-language
+content-length
+content-location
+content-md5
+content-range
+content-type
+connection
+date
+expect
+max-forwards
+pragma
+proxy-authorization
+referer
+te
+transfer-encoding
+user-agent
+via
+```
+
+Note that, when using a tunneling proxy, the `proxy-authorization`
+header and any headers from custom `proxyHeaderExclusiveList` are
+*never* sent to the endpoint server, but only to the proxy server.
+
+
+### Controlling proxy behaviour using environment variables
+
+The following environment variables are respected by `request`:
+
+ * `HTTP_PROXY` / `http_proxy`
+ * `HTTPS_PROXY` / `https_proxy`
+ * `NO_PROXY` / `no_proxy`
+
+When `HTTP_PROXY` / `http_proxy` are set, they will be used to proxy non-SSL requests that do not have an explicit `proxy` configuration option present. Similarly, `HTTPS_PROXY` / `https_proxy` will be respected for SSL requests that do not have an explicit `proxy` configuration option. It is valid to define a proxy in one of the environment variables, but then override it for a specific request, using the `proxy` configuration option. Furthermore, the `proxy` configuration option can be explicitly set to false / null to opt out of proxying altogether for that request.
+
+`request` is also aware of the `NO_PROXY`/`no_proxy` environment variables. These variables provide a granular way to opt out of proxying, on a per-host basis. It should contain a comma separated list of hosts to opt out of proxying. It is also possible to opt of proxying when a particular destination port is used. Finally, the variable may be set to `*` to opt out of the implicit proxy configuration of the other environment variables.
+
+Here's some examples of valid `no_proxy` values:
+
+ * `google.com` - don't proxy HTTP/HTTPS requests to Google.
+ * `google.com:443` - don't proxy HTTPS requests to Google, but *do* proxy HTTP requests to Google.
+ * `google.com:443, yahoo.com:80` - don't proxy HTTPS requests to Google, and don't proxy HTTP requests to Yahoo!
+ * `*` - ignore `https_proxy`/`http_proxy` environment variables altogether.
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## UNIX Domain Sockets
+
+`request` supports making requests to [UNIX Domain Sockets](http://en.wikipedia.org/wiki/Unix_domain_socket). To make one, use the following URL scheme:
+
+```js
+/* Pattern */ 'http://unix:SOCKET:PATH'
+/* Example */ request.get('http://unix:/absolute/path/to/unix.socket:/request/path')
+```
+
+Note: The `SOCKET` path is assumed to be absolute to the root of the host file system.
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## TLS/SSL Protocol
+
+TLS/SSL Protocol options, such as `cert`, `key` and `passphrase`, can be
+set in the `agentOptions` property of the `options` object.
+In the example below, we call an API requires client side SSL certificate
+(in PEM format) with passphrase protected private key (in PEM format) and disable the SSLv3 protocol:
+
+```js
+var fs = require('fs')
+ , path = require('path')
+ , certFile = path.resolve(__dirname, 'ssl/client.crt')
+ , keyFile = path.resolve(__dirname, 'ssl/client.key')
+ , request = require('request');
+
+var options = {
+ url: 'https://api.some-server.com/',
+ agentOptions: {
+ cert: fs.readFileSync(certFile),
+ key: fs.readFileSync(keyFile),
+ // Or use `pfx` property replacing `cert` and `key` when using private key, certificate and CA certs in PFX or PKCS12 format:
+ // pfx: fs.readFileSync(pfxFilePath),
+ passphrase: 'password',
+ securityOptions: 'SSL_OP_NO_SSLv3'
+ }
+};
+
+request.get(options);
+```
+
+It is able to force using SSLv3 only by specifying `secureProtocol`:
+
+```js
+request.get({
+ url: 'https://api.some-server.com/',
+ agentOptions: {
+ secureProtocol: 'SSLv3_method'
+ }
+});
+```
+
+It is possible to accept other certificates than those signed by generally allowed Certificate Authorities (CAs).
+This can be useful, for example, when using self-signed certificates.
+To allow a different certificate, you can specify the signing CA by adding the contents of the CA's certificate file to the `agentOptions`:
+
+```js
+request.get({
+ url: 'https://api.some-server.com/',
+ agentOptions: {
+ ca: fs.readFileSync('ca.cert.pem')
+ }
+});
+```
+
+[back to top](#table-of-contents)
+
+
+---
+
+## Support for HAR 1.2
+
+The `options.har` property will override the values: `url`, `method`, `qs`, `headers`, `form`, `formData`, `body`, `json`, as well as construct multipart data and read files from disk when `request.postData.params[].fileName` is present without a matching `value`.
+
+a validation step will check if the HAR Request format matches the latest spec (v1.2) and will skip parsing if not matching.
+
+```js
+ var request = require('request')
+ request({
+ // will be ignored
+ method: 'GET'
+ uri: 'http://www.google.com',
+
+ // HTTP Archive Request Object
+ har: {
+ url: 'http://www.mockbin.com/har'
+ method: 'POST',
+ headers: [
+ {
+ name: 'content-type',
+ value: 'application/x-www-form-urlencoded'
+ }
+ ],
+ postData: {
+ mimeType: 'application/x-www-form-urlencoded',
+ params: [
+ {
+ name: 'foo',
+ value: 'bar'
+ },
+ {
+ name: 'hello',
+ value: 'world'
+ }
+ ]
+ }
+ }
+ })
+
+ // a POST request will be sent to http://www.mockbin.com
+ // with body an application/x-www-form-urlencoded body:
+ // foo=bar&hello=world
+```
+
+[back to top](#table-of-contents)
+
+
+---
+
+## request(options, callback)
+
+The first argument can be either a `url` or an `options` object. The only required option is `uri`; all others are optional.
+
+- `uri` || `url` - fully qualified uri or a parsed url object from `url.parse()`
+- `baseUrl` - fully qualified uri string used as the base url. Most useful with `request.defaults`, for example when you want to do many requests to the same domain. If `baseUrl` is `https://example.com/api/`, then requesting `/end/point?test=true` will fetch `https://example.com/api/end/point?test=true`. When `baseUrl` is given, `uri` must also be a string.
+- `method` - http method (default: `"GET"`)
+- `headers` - http headers (default: `{}`)
+
+---
+
+- `qs` - object containing querystring values to be appended to the `uri`
+- `qsParseOptions` - object containing options to pass to the [qs.parse](https://github.com/hapijs/qs#parsing-objects) method or [querystring.parse](https://nodejs.org/docs/v0.12.0/api/querystring.html#querystring_querystring_parse_str_sep_eq_options) method
+- `qsStringifyOptions` - object containing options to pass to the [qs.stringify](https://github.com/hapijs/qs#stringifying) method or to the [querystring.stringify](https://nodejs.org/docs/v0.12.0/api/querystring.html#querystring_querystring_stringify_obj_sep_eq_options) method. For example, to change the way arrays are converted to query strings pass the `arrayFormat` option with one of `indices|brackets|repeat`
+- `useQuerystring` - If true, use `querystring` to stringify and parse
+ querystrings, otherwise use `qs` (default: `false`). Set this option to
+ `true` if you need arrays to be serialized as `foo=bar&foo=baz` instead of the
+ default `foo[0]=bar&foo[1]=baz`.
+
+---
+
+- `body` - entity body for PATCH, POST and PUT requests. Must be a `Buffer` or `String`, unless `json` is `true`. If `json` is `true`, then `body` must be a JSON-serializable object.
+- `form` - when passed an object or a querystring, this sets `body` to a querystring representation of value, and adds `Content-type: application/x-www-form-urlencoded` header. When passed no options, a `FormData` instance is returned (and is piped to request). See "Forms" section above.
+- `formData` - Data to pass for a `multipart/form-data` request. See
+ [Forms](#forms) section above.
+- `multipart` - array of objects which contain their own headers and `body`
+ attributes. Sends a `multipart/related` request. See [Forms](#forms) section
+ above.
+ - Alternatively you can pass in an object `{chunked: false, data: []}` where
+ `chunked` is used to specify whether the request is sent in
+ [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)
+ In non-chunked requests, data items with body streams are not allowed.
+- `preambleCRLF` - append a newline/CRLF before the boundary of your `multipart/form-data` request.
+- `postambleCRLF` - append a newline/CRLF at the end of the boundary of your `multipart/form-data` request.
+- `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json` header. Additionally, parses the response body as JSON.
+- `jsonReviver` - a [reviver function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) that will be passed to `JSON.parse()` when parsing a JSON response body.
+
+---
+
+- `auth` - A hash containing values `user` || `username`, `pass` || `password`, and `sendImmediately` (optional). See documentation above.
+- `oauth` - Options for OAuth HMAC-SHA1 signing. See documentation above.
+- `hawk` - Options for [Hawk signing](https://github.com/hueniverse/hawk). The `credentials` key must contain the necessary signing info, [see hawk docs for details](https://github.com/hueniverse/hawk#usage-example).
+- `aws` - `object` containing AWS signing information. Should have the properties `key`, `secret`. Also requires the property `bucket`, unless you’re specifying your `bucket` as part of the path, or the request doesn’t use a bucket (i.e. GET Services)
+- `httpSignature` - Options for the [HTTP Signature Scheme](https://github.com/joyent/node-http-signature/blob/master/http_signing.md) using [Joyent's library](https://github.com/joyent/node-http-signature). The `keyId` and `key` properties must be specified. See the docs for other options.
+
+---
+
+- `followRedirect` - follow HTTP 3xx responses as redirects (default: `true`). This property can also be implemented as function which gets `response` object as a single argument and should return `true` if redirects should continue or `false` otherwise.
+- `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects (default: `false`)
+- `maxRedirects` - the maximum number of redirects to follow (default: `10`)
+
+---
+
+- `encoding` - Encoding to be used on `setEncoding` of response data. If `null`, the `body` is returned as a `Buffer`. Anything else **(including the default value of `undefined`)** will be passed as the [encoding](http://nodejs.org/api/buffer.html#buffer_buffer) parameter to `toString()` (meaning this is effectively `utf8` by default).
+- `gzip` - If `true`, add an `Accept-Encoding` header to request compressed content encodings from the server (if not already present) and decode supported content encodings in the response. **Note:** Automatic decoding of the response content is performed on the body data returned through `request` (both through the `request` stream and passed to the callback function) but is not performed on the `response` stream (available from the `response` event) which is the unmodified `http.IncomingMessage` object which may contain compressed data. See example below.
+- `jar` - If `true` and `tough-cookie` is installed, remember cookies for future use (or define your custom cookie jar; see examples section)
+
+---
+
+- `pool` - An object describing which agents to use for the request. If this option is omitted the request will use the global agent (as long as [your options allow for it](request.js#L747)). Otherwise, request will search the pool for your custom agent. If no custom agent is found, a new agent will be created and added to the pool.
+ - A `maxSockets` property can also be provided on the `pool` object to set the max number of sockets for all agents created (ex: `pool: {maxSockets: Infinity}`).
+ - Note that if you are sending multiple requests in a loop and creating
+ multiple new `pool` objects, `maxSockets` will not work as intended. To
+ work around this, either use [`request.defaults`](#requestdefaultsoptions)
+ with your pool options or create the pool object with the `maxSockets`
+ property outside of the loop.
+- `timeout` - Integer containing the number of milliseconds to wait for a
+ request to respond before aborting the request. Note that if the underlying
+ TCP connection cannot be established, the OS-wide TCP connection timeout will
+ overrule the `timeout` option ([the default in Linux is around 20 seconds](http://www.sekuda.com/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout)).
+- `localAddress` - Local interface to bind for network connections.
+- `proxy` - An HTTP proxy to be used. Supports proxy Auth with Basic Auth, identical to support for the `url` parameter (by embedding the auth info in the `uri`)
+- `strictSSL` - If `true`, requires SSL certificates be valid. **Note:** to use your own certificate authority, you need to specify an agent that was created with that CA as an option.
+- `agentOptions` - Object containing user agent options. See documentation above. **Note:** [see tls API doc for TLS/SSL options](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
+- `tunnel` - controls the behavior of
+ [HTTP `CONNECT` tunneling](https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling)
+ as follows:
+ - `undefined` (default) - `true` if the destination is `https` or a previous
+ request in the redirect chain used a tunneling proxy, `false` otherwise
+ - `true` - always tunnel to the destination by making a `CONNECT` request to
+ the proxy
+ - `false` - request the destination as a `GET` request.
+- `proxyHeaderWhiteList` - A whitelist of headers to send to a
+ tunneling proxy.
+- `proxyHeaderExclusiveList` - A whitelist of headers to send
+ exclusively to a tunneling proxy and not to destination.
+- `removeRefererHeader` - removes the referer header when a redirect happens (default: `false`).
+
+---
+
+- `time` - If `true`, the request-response cycle (including all redirects) is timed at millisecond resolution, and the result provided on the response's `elapsedTime` property.
+
+---
+
+- `har` - A [HAR 1.2 Request Object](http://www.softwareishard.com/blog/har-12-spec/#request), will be processed from HAR format into options overwriting matching values *(see the [HAR 1.2 section](#support-for-har-1.2) for details)*
+
+The callback argument gets 3 arguments:
+
+1. An `error` when applicable (usually from [`http.ClientRequest`](http://nodejs.org/api/http.html#http_class_http_clientrequest) object)
+2. An [`http.IncomingMessage`](http://nodejs.org/api/http.html#http_http_incomingmessage) object
+3. The third is the `response` body (`String` or `Buffer`, or JSON object if the `json` option is supplied)
+
+[back to top](#table-of-contents)
+
+
+---
+
+## Convenience methods
+
+There are also shorthand methods for different HTTP METHODs and some other conveniences.
+
+
+### request.defaults(options)
+
+This method **returns a wrapper** around the normal request API that defaults
+to whatever options you pass to it.
+
+**Note:** `request.defaults()` **does not** modify the global request API;
+instead, it **returns a wrapper** that has your default settings applied to it.
+
+**Note:** You can call `.defaults()` on the wrapper that is returned from
+`request.defaults` to add/override defaults that were previously defaulted.
+
+For example:
+```js
+//requests using baseRequest() will set the 'x-token' header
+var baseRequest = request.defaults({
+ headers: {x-token: 'my-token'}
+})
+
+//requests using specialRequest() will include the 'x-token' header set in
+//baseRequest and will also include the 'special' header
+var specialRequest = baseRequest.defaults({
+ headers: {special: 'special value'}
+})
+```
+
+### request.put
+
+Same as `request()`, but defaults to `method: "PUT"`.
+
+```js
+request.put(url)
+```
+
+### request.patch
+
+Same as `request()`, but defaults to `method: "PATCH"`.
+
+```js
+request.patch(url)
+```
+
+### request.post
+
+Same as `request()`, but defaults to `method: "POST"`.
+
+```js
+request.post(url)
+```
+
+### request.head
+
+Same as `request()`, but defaults to `method: "HEAD"`.
+
+```js
+request.head(url)
+```
+
+### request.del
+
+Same as `request()`, but defaults to `method: "DELETE"`.
+
+```js
+request.del(url)
+```
+
+### request.get
+
+Same as `request()` (for uniformity).
+
+```js
+request.get(url)
+```
+### request.cookie
+
+Function that creates a new cookie.
+
+```js
+request.cookie('key1=value1')
+```
+### request.jar()
+
+Function that creates a new cookie jar.
+
+```js
+request.jar()
+```
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## Debugging
+
+There are at least three ways to debug the operation of `request`:
+
+1. Launch the node process like `NODE_DEBUG=request node script.js`
+ (`lib,request,otherlib` works too).
+
+2. Set `require('request').debug = true` at any time (this does the same thing
+ as #1).
+
+3. Use the [request-debug module](https://github.com/nylen/request-debug) to
+ view request and response headers and bodies.
+
+[back to top](#table-of-contents)
+
+
+---
+
+
+## Examples:
+
+```js
+ var request = require('request')
+ , rand = Math.floor(Math.random()*100000000).toString()
+ ;
+ request(
+ { method: 'PUT'
+ , uri: 'http://mikeal.iriscouch.com/testjs/' + rand
+ , multipart:
+ [ { 'content-type': 'application/json'
+ , body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})
+ }
+ , { body: 'I am an attachment' }
+ ]
+ }
+ , function (error, response, body) {
+ if(response.statusCode == 201){
+ console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand)
+ } else {
+ console.log('error: '+ response.statusCode)
+ console.log(body)
+ }
+ }
+ )
+```
+
+For backwards-compatibility, response compression is not supported by default.
+To accept gzip-compressed responses, set the `gzip` option to `true`. Note
+that the body data passed through `request` is automatically decompressed
+while the response object is unmodified and will contain compressed data if
+the server sent a compressed response.
+
+```js
+ var request = require('request')
+ request(
+ { method: 'GET'
+ , uri: 'http://www.google.com'
+ , gzip: true
+ }
+ , function (error, response, body) {
+ // body is the decompressed response body
+ console.log('server encoded the data as: ' + (response.headers['content-encoding'] || 'identity'))
+ console.log('the decoded data is: ' + body)
+ }
+ ).on('data', function(data) {
+ // decompressed data as it is received
+ console.log('decoded chunk: ' + data)
+ })
+ .on('response', function(response) {
+ // unmodified http.IncomingMessage object
+ response.on('data', function(data) {
+ // compressed data as it is received
+ console.log('received ' + data.length + ' bytes of compressed data')
+ })
+ })
+```
+
+Cookies are disabled by default (else, they would be used in subsequent requests). To enable cookies, set `jar` to `true` (either in `defaults` or `options`) and install `tough-cookie`.
+
+```js
+var request = request.defaults({jar: true})
+request('http://www.google.com', function () {
+ request('http://images.google.com')
+})
+```
+
+To use a custom cookie jar (instead of `request`’s global cookie jar), set `jar` to an instance of `request.jar()` (either in `defaults` or `options`)
+
+```js
+var j = request.jar()
+var request = request.defaults({jar:j})
+request('http://www.google.com', function () {
+ request('http://images.google.com')
+})
+```
+
+OR
+
+```js
+var j = request.jar();
+var cookie = request.cookie('key1=value1');
+var url = 'http://www.google.com';
+j.setCookie(cookie, url);
+request({url: url, jar: j}, function () {
+ request('http://images.google.com')
+})
+```
+
+To use a custom cookie store (such as a
+[`FileCookieStore`](https://github.com/mitsuru/tough-cookie-filestore)
+which supports saving to and restoring from JSON files), pass it as a parameter
+to `request.jar()`:
+
+```js
+var FileCookieStore = require('tough-cookie-filestore');
+// NOTE - currently the 'cookies.json' file must already exist!
+var j = request.jar(new FileCookieStore('cookies.json'));
+request = request.defaults({ jar : j })
+request('http://www.google.com', function() {
+ request('http://images.google.com')
+})
+```
+
+The cookie store must be a
+[`tough-cookie`](https://github.com/goinstant/tough-cookie)
+store and it must support synchronous operations; see the
+[`CookieStore` API docs](https://github.com/goinstant/tough-cookie/#cookiestore-api)
+for details.
+
+To inspect your cookie jar after a request:
+
+```js
+var j = request.jar()
+request({url: 'http://www.google.com', jar: j}, function () {
+ var cookie_string = j.getCookieString(uri); // "key1=value1; key2=value2; ..."
+ var cookies = j.getCookies(uri);
+ // [{key: 'key1', value: 'value1', domain: "www.google.com", ...}, ...]
+})
+```
+
+[back to top](#table-of-contents)
--- /dev/null
+# http://www.appveyor.com/docs/appveyor-yml
+
+# Fix line endings in Windows. (runs before repo cloning)
+init:
+ - git config --global core.autocrlf input
+
+# Test against these versions of Node.js.
+environment:
+ matrix:
+ - nodejs_version: "0.10"
+ - nodejs_version: "0.8"
+ - nodejs_version: "0.11"
+
+# Allow failing jobs for bleeding-edge Node.js versions.
+matrix:
+ allow_failures:
+ - nodejs_version: "0.11"
+
+# Install scripts. (runs after repo cloning)
+install:
+ # Get the latest stable version of Node 0.STABLE.latest
+ - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)
+ # Typical npm stuff.
+ - npm install
+
+# Post-install test scripts.
+test_script:
+ # Output useful info for debugging.
+ - ps: "npm test # PowerShell" # Pass comment to PS for easier debugging
+ - cmd: npm test
+
+# Don't actually build.
+build: off
+
+# Set build version format here instead of in the admin panel.
+version: "{build}"
--- /dev/null
+
+# Authentication
+
+## OAuth
+
+### OAuth1.0 Refresh Token
+
+- http://oauth.googlecode.com/svn/spec/ext/session/1.0/drafts/1/spec.html#anchor4
+- https://developer.yahoo.com/oauth/guide/oauth-refreshaccesstoken.html
+
+```js
+request.post('https://api.login.yahoo.com/oauth/v2/get_token', {
+ oauth: {
+ consumer_key: '...',
+ consumer_secret: '...',
+ token: '...',
+ token_secret: '...',
+ session_handle: '...'
+ }
+}, function (err, res, body) {
+ var result = require('querystring').parse(body)
+ // assert.equal(typeof result, 'object')
+})
+```
+
+### OAuth2 Refresh Token
+
+- https://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-6
+
+```js
+request.post('https://accounts.google.com/o/oauth2/token', {
+ form: {
+ grant_type: 'refresh_token',
+ client_id: '...',
+ client_secret: '...',
+ refresh_token: '...'
+ },
+ json: true
+}, function (err, res, body) {
+ // assert.equal(typeof body, 'object')
+})
+```
+
+# Multipart
+
+## multipart/form-data
+
+### Flickr Image Upload
+
+- https://www.flickr.com/services/api/upload.api.html
+
+```js
+request.post('https://up.flickr.com/services/upload', {
+ oauth: {
+ consumer_key: '...',
+ consumer_secret: '...',
+ token: '...',
+ token_secret: '...'
+ },
+ // all meta data should be included here for proper signing
+ qs: {
+ title: 'My cat is awesome',
+ description: 'Sent on ' + new Date(),
+ is_public: 1
+ },
+ // again the same meta data + the actual photo
+ formData: {
+ title: 'My cat is awesome',
+ description: 'Sent on ' + new Date(),
+ is_public: 1,
+ photo:fs.createReadStream('cat.png')
+ },
+ json: true
+}, function (err, res, body) {
+ // assert.equal(typeof body, 'object')
+})
+```
+
+# Streams
+
+## `POST` data
+
+Use Request as a Writable stream to easily `POST` Readable streams (like files, other HTTP requests, or otherwise).
+
+TL;DR: Pipe a Readable Stream onto Request via:
+
+```
+READABLE.pipe(request.post(URL));
+```
+
+A more detailed example:
+
+```js
+var fs = require('fs')
+ , path = require('path')
+ , http = require('http')
+ , request = require('request')
+ , TMP_FILE_PATH = path.join(path.sep, 'tmp', 'foo')
+;
+
+// write a temporary file:
+fs.writeFileSync(TMP_FILE_PATH, 'foo bar baz quk\n');
+
+http.createServer(function(req, res) {
+ console.log('the server is receiving data!\n');
+ req
+ .on('end', res.end.bind(res))
+ .pipe(process.stdout)
+ ;
+}).listen(3000).unref();
+
+fs.createReadStream(TMP_FILE_PATH)
+ .pipe(request.post('http://127.0.0.1:3000'))
+;
+```
--- /dev/null
+// Copyright 2010-2012 Mikeal Rogers
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+'use strict'
+
+var extend = require('util')._extend
+ , cookies = require('./lib/cookies')
+ , helpers = require('./lib/helpers')
+
+var isFunction = helpers.isFunction
+ , paramsHaveRequestBody = helpers.paramsHaveRequestBody
+
+
+// organize params for patch, post, put, head, del
+function initParams(uri, options, callback) {
+ if (typeof options === 'function') {
+ callback = options
+ }
+
+ var params = {}
+ if (typeof options === 'object') {
+ params = extend({}, options)
+ params = extend(params, {uri: uri})
+ } else if (typeof uri === 'string') {
+ params = extend({}, {uri: uri})
+ } else {
+ params = extend({}, uri)
+ }
+
+ params.callback = callback
+ return params
+}
+
+function request (uri, options, callback) {
+ if (typeof uri === 'undefined') {
+ throw new Error('undefined is not a valid uri or options object.')
+ }
+
+ var params = initParams(uri, options, callback)
+
+ if (params.method === 'HEAD' && paramsHaveRequestBody(params)) {
+ throw new Error('HTTP HEAD requests MUST NOT include a request body.')
+ }
+
+ return new request.Request(params)
+}
+
+var verbs = ['get', 'head', 'post', 'put', 'patch', 'del']
+
+verbs.forEach(function(verb) {
+ var method = verb === 'del' ? 'DELETE' : verb.toUpperCase()
+ request[verb] = function (uri, options, callback) {
+ var params = initParams(uri, options, callback)
+ params.method = method
+ return request(params, params.callback)
+ }
+})
+
+request.jar = function (store) {
+ return cookies.jar(store)
+}
+
+request.cookie = function (str) {
+ return cookies.parse(str)
+}
+
+function wrapRequestMethod (method, options, requester) {
+
+ return function (uri, opts, callback) {
+ var params = initParams(uri, opts, callback)
+
+ var headerlessOptions = extend({}, options)
+ delete headerlessOptions.headers
+ params = extend(headerlessOptions, params)
+
+ if (options.headers) {
+ var headers = extend({}, options.headers)
+ params.headers = extend(headers, params.headers)
+ }
+
+ if (typeof method === 'string') {
+ params.method = (method === 'del' ? 'DELETE' : method.toUpperCase())
+ method = request[method]
+ }
+
+ if (isFunction(requester)) {
+ method = requester
+ }
+
+ return method(params, params.callback)
+ }
+}
+
+request.defaults = function (options, requester) {
+ var self = this
+
+ if (typeof options === 'function') {
+ requester = options
+ options = {}
+ }
+
+ var defaults = wrapRequestMethod(self, options, requester)
+
+ var verbs = ['get', 'head', 'post', 'put', 'patch', 'del']
+ verbs.forEach(function(verb) {
+ defaults[verb] = wrapRequestMethod(verb, options, requester)
+ })
+
+ defaults.cookie = wrapRequestMethod(self.cookie, options, requester)
+ defaults.jar = self.jar
+ defaults.defaults = self.defaults
+ return defaults
+}
+
+request.forever = function (agentOptions, optionsArg) {
+ var options = {}
+ if (optionsArg) {
+ options = extend({}, optionsArg)
+ }
+ if (agentOptions) {
+ options.agentOptions = agentOptions
+ }
+
+ options.forever = true
+ return request.defaults(options)
+}
+
+// Exports
+
+module.exports = request
+request.Request = require('./request')
+request.initParams = initParams
+
+// Backwards compatibility for request.debug
+Object.defineProperty(request, 'debug', {
+ enumerable : true,
+ get : function() {
+ return request.Request.debug
+ },
+ set : function(debug) {
+ request.Request.debug = debug
+ }
+})
--- /dev/null
+'use strict'
+
+var caseless = require('caseless')
+ , uuid = require('node-uuid')
+ , helpers = require('./helpers')
+
+var md5 = helpers.md5
+ , toBase64 = helpers.toBase64
+
+
+function Auth (request) {
+ // define all public properties here
+ this.request = request
+ this.hasAuth = false
+ this.sentAuth = false
+ this.bearerToken = null
+ this.user = null
+ this.pass = null
+}
+
+Auth.prototype.basic = function (user, pass, sendImmediately) {
+ var self = this
+ if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) {
+ throw new Error('auth() received invalid user or password')
+ }
+ self.user = user
+ self.pass = pass
+ self.hasAuth = true
+ var header = user + ':' + (pass || '')
+ if (sendImmediately || typeof sendImmediately === 'undefined') {
+ var authHeader = 'Basic ' + toBase64(header)
+ self.sentAuth = true
+ return authHeader
+ }
+}
+
+Auth.prototype.bearer = function (bearer, sendImmediately) {
+ var self = this
+ self.bearerToken = bearer
+ self.hasAuth = true
+ if (sendImmediately || typeof sendImmediately === 'undefined') {
+ if (typeof bearer === 'function') {
+ bearer = bearer()
+ }
+ var authHeader = 'Bearer ' + (bearer || '')
+ self.sentAuth = true
+ return authHeader
+ }
+}
+
+Auth.prototype.digest = function (method, path, authHeader) {
+ // TODO: More complete implementation of RFC 2617.
+ // - check challenge.algorithm
+ // - support algorithm="MD5-sess"
+ // - handle challenge.domain
+ // - support qop="auth-int" only
+ // - handle Authentication-Info (not necessarily?)
+ // - check challenge.stale (not necessarily?)
+ // - increase nc (not necessarily?)
+ // For reference:
+ // http://tools.ietf.org/html/rfc2617#section-3
+ // https://github.com/bagder/curl/blob/master/lib/http_digest.c
+
+ var self = this
+
+ var challenge = {}
+ var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
+ for (;;) {
+ var match = re.exec(authHeader)
+ if (!match) {
+ break
+ }
+ challenge[match[1]] = match[2] || match[3]
+ }
+
+ var ha1 = md5(self.user + ':' + challenge.realm + ':' + self.pass)
+ var ha2 = md5(method + ':' + path)
+ var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
+ var nc = qop && '00000001'
+ var cnonce = qop && uuid().replace(/-/g, '')
+ var digestResponse = qop
+ ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
+ : md5(ha1 + ':' + challenge.nonce + ':' + ha2)
+ var authValues = {
+ username: self.user,
+ realm: challenge.realm,
+ nonce: challenge.nonce,
+ uri: path,
+ qop: qop,
+ response: digestResponse,
+ nc: nc,
+ cnonce: cnonce,
+ algorithm: challenge.algorithm,
+ opaque: challenge.opaque
+ }
+
+ authHeader = []
+ for (var k in authValues) {
+ if (authValues[k]) {
+ if (k === 'qop' || k === 'nc' || k === 'algorithm') {
+ authHeader.push(k + '=' + authValues[k])
+ } else {
+ authHeader.push(k + '="' + authValues[k] + '"')
+ }
+ }
+ }
+ authHeader = 'Digest ' + authHeader.join(', ')
+ self.sentAuth = true
+ return authHeader
+}
+
+Auth.prototype.onRequest = function (user, pass, sendImmediately, bearer) {
+ var self = this
+ , request = self.request
+
+ var authHeader
+ if (bearer === undefined && user === undefined) {
+ throw new Error('no auth mechanism defined')
+ } else if (bearer !== undefined) {
+ authHeader = self.bearer(bearer, sendImmediately)
+ } else {
+ authHeader = self.basic(user, pass, sendImmediately)
+ }
+ if (authHeader) {
+ request.setHeader('authorization', authHeader)
+ }
+}
+
+Auth.prototype.onResponse = function (response) {
+ var self = this
+ , request = self.request
+
+ if (!self.hasAuth || self.sentAuth) { return null }
+
+ var c = caseless(response.headers)
+
+ var authHeader = c.get('www-authenticate')
+ var authVerb = authHeader && authHeader.split(' ')[0].toLowerCase()
+ // debug('reauth', authVerb)
+
+ switch (authVerb) {
+ case 'basic':
+ return self.basic(self.user, self.pass, true)
+
+ case 'bearer':
+ return self.bearer(self.bearerToken, true)
+
+ case 'digest':
+ return self.digest(request.method, request.path, authHeader)
+ }
+}
+
+exports.Auth = Auth
--- /dev/null
+'use strict'
+
+var tough = require('tough-cookie')
+
+var Cookie = tough.Cookie
+ , CookieJar = tough.CookieJar
+
+
+exports.parse = function(str) {
+ if (str && str.uri) {
+ str = str.uri
+ }
+ if (typeof str !== 'string') {
+ throw new Error('The cookie function only accepts STRING as param')
+ }
+ return Cookie.parse(str)
+}
+
+// Adapt the sometimes-Async api of tough.CookieJar to our requirements
+function RequestJar(store) {
+ var self = this
+ self._jar = new CookieJar(store)
+}
+RequestJar.prototype.setCookie = function(cookieOrStr, uri, options) {
+ var self = this
+ return self._jar.setCookieSync(cookieOrStr, uri, options || {})
+}
+RequestJar.prototype.getCookieString = function(uri) {
+ var self = this
+ return self._jar.getCookieStringSync(uri)
+}
+RequestJar.prototype.getCookies = function(uri) {
+ var self = this
+ return self._jar.getCookiesSync(uri)
+}
+
+exports.jar = function(store) {
+ return new RequestJar(store)
+}
--- /dev/null
+'use strict'
+
+module.exports =
+function copy (obj) {
+ var o = {}
+ Object.keys(obj).forEach(function (i) {
+ o[i] = obj[i]
+ })
+ return o
+}
--- /dev/null
+'use strict'
+
+function formatHostname(hostname) {
+ // canonicalize the hostname, so that 'oogle.com' won't match 'google.com'
+ return hostname.replace(/^\.*/, '.').toLowerCase()
+}
+
+function parseNoProxyZone(zone) {
+ zone = zone.trim().toLowerCase()
+
+ var zoneParts = zone.split(':', 2)
+ , zoneHost = formatHostname(zoneParts[0])
+ , zonePort = zoneParts[1]
+ , hasPort = zone.indexOf(':') > -1
+
+ return {hostname: zoneHost, port: zonePort, hasPort: hasPort}
+}
+
+function uriInNoProxy(uri, noProxy) {
+ var port = uri.port || (uri.protocol === 'https:' ? '443' : '80')
+ , hostname = formatHostname(uri.hostname)
+ , noProxyList = noProxy.split(',')
+
+ // iterate through the noProxyList until it finds a match.
+ return noProxyList.map(parseNoProxyZone).some(function(noProxyZone) {
+ var isMatchedAt = hostname.indexOf(noProxyZone.hostname)
+ , hostnameMatched = (
+ isMatchedAt > -1 &&
+ (isMatchedAt === hostname.length - noProxyZone.hostname.length)
+ )
+
+ if (noProxyZone.hasPort) {
+ return (port === noProxyZone.port) && hostnameMatched
+ }
+
+ return hostnameMatched
+ })
+}
+
+function getProxyFromURI(uri) {
+ // Decide the proper request proxy to use based on the request URI object and the
+ // environmental variables (NO_PROXY, HTTP_PROXY, etc.)
+ // respect NO_PROXY environment variables (see: http://lynx.isc.org/current/breakout/lynx_help/keystrokes/environments.html)
+
+ var noProxy = process.env.NO_PROXY || process.env.no_proxy || ''
+
+ // if the noProxy is a wildcard then return null
+
+ if (noProxy === '*') {
+ return null
+ }
+
+ // if the noProxy is not empty and the uri is found return null
+
+ if (noProxy !== '' && uriInNoProxy(uri, noProxy)) {
+ return null
+ }
+
+ // Check for HTTP or HTTPS Proxy in environment Else default to null
+
+ if (uri.protocol === 'http:') {
+ return process.env.HTTP_PROXY ||
+ process.env.http_proxy || null
+ }
+
+ if (uri.protocol === 'https:') {
+ return process.env.HTTPS_PROXY ||
+ process.env.https_proxy ||
+ process.env.HTTP_PROXY ||
+ process.env.http_proxy || null
+ }
+
+ // if none of that works, return null
+ // (What uri protocol are you using then?)
+
+ return null
+}
+
+module.exports = getProxyFromURI
--- /dev/null
+'use strict'
+
+var fs = require('fs')
+var qs = require('querystring')
+var validate = require('har-validator')
+var util = require('util')
+
+function Har (request) {
+ this.request = request
+}
+
+Har.prototype.reducer = function (obj, pair) {
+ // new property ?
+ if (obj[pair.name] === undefined) {
+ obj[pair.name] = pair.value
+ return obj
+ }
+
+ // existing? convert to array
+ var arr = [
+ obj[pair.name],
+ pair.value
+ ]
+
+ obj[pair.name] = arr
+
+ return obj
+}
+
+Har.prototype.prep = function (data) {
+ // construct utility properties
+ data.queryObj = {}
+ data.headersObj = {}
+ data.postData.jsonObj = false
+ data.postData.paramsObj = false
+
+ // construct query objects
+ if (data.queryString && data.queryString.length) {
+ data.queryObj = data.queryString.reduce(this.reducer, {})
+ }
+
+ // construct headers objects
+ if (data.headers && data.headers.length) {
+ // loweCase header keys
+ data.headersObj = data.headers.reduceRight(function (headers, header) {
+ headers[header.name] = header.value
+ return headers
+ }, {})
+ }
+
+ // construct Cookie header
+ if (data.cookies && data.cookies.length) {
+ var cookies = data.cookies.map(function (cookie) {
+ return cookie.name + '=' + cookie.value
+ })
+
+ if (cookies.length) {
+ data.headersObj.cookie = cookies.join('; ')
+ }
+ }
+
+ // prep body
+ switch (data.postData.mimeType) {
+ case 'multipart/mixed':
+ case 'multipart/related':
+ case 'multipart/form-data':
+ case 'multipart/alternative':
+ // reset values
+ data.postData.mimeType = 'multipart/form-data'
+ break
+
+ case 'application/x-www-form-urlencoded':
+ if (!data.postData.params) {
+ data.postData.text = ''
+ } else {
+ data.postData.paramsObj = data.postData.params.reduce(this.reducer, {})
+
+ // always overwrite
+ data.postData.text = qs.stringify(data.postData.paramsObj)
+ }
+ break
+
+ case 'text/json':
+ case 'text/x-json':
+ case 'application/json':
+ case 'application/x-json':
+ data.postData.mimeType = 'application/json'
+
+ if (data.postData.text) {
+ try {
+ data.postData.jsonObj = JSON.parse(data.postData.text)
+ } catch (e) {
+ this.request.debug(e)
+
+ // force back to text/plain
+ data.postData.mimeType = 'text/plain'
+ }
+ }
+ break
+ }
+
+ return data
+}
+
+Har.prototype.options = function (options) {
+ // skip if no har property defined
+ if (!options.har) {
+ return options
+ }
+
+ var har = util._extend({}, options.har)
+
+ // only process the first entry
+ if (har.log && har.log.entries) {
+ har = har.log.entries[0]
+ }
+
+ // add optional properties to make validation successful
+ har.url = har.url || options.url || options.uri || options.baseUrl || '/'
+ har.httpVersion = har.httpVersion || 'HTTP/1.1'
+ har.queryString = har.queryString || []
+ har.headers = har.headers || []
+ har.cookies = har.cookies || []
+ har.postData = har.postData || {}
+ har.postData.mimeType = har.postData.mimeType || 'application/octet-stream'
+
+ har.bodySize = 0
+ har.headersSize = 0
+ har.postData.size = 0
+
+ if (!validate.request(har)) {
+ return options
+ }
+
+ // clean up and get some utility properties
+ var req = this.prep(har)
+
+ // construct new options
+ if (req.url) {
+ options.url = req.url
+ }
+
+ if (req.method) {
+ options.method = req.method
+ }
+
+ if (Object.keys(req.queryObj).length) {
+ options.qs = req.queryObj
+ }
+
+ if (Object.keys(req.headersObj).length) {
+ options.headers = req.headersObj
+ }
+
+ switch (req.postData.mimeType) {
+ case 'application/x-www-form-urlencoded':
+ options.form = req.postData.paramsObj
+ break
+
+ case 'application/json':
+ if (req.postData.jsonObj) {
+ options.body = req.postData.jsonObj
+ options.json = true
+ }
+ break
+
+ case 'multipart/form-data':
+ options.formData = {}
+
+ req.postData.params.forEach(function (param) {
+ var attachment = {}
+
+ if (!param.fileName && !param.fileName && !param.contentType) {
+ options.formData[param.name] = param.value
+ return
+ }
+
+ // attempt to read from disk!
+ if (param.fileName && !param.value) {
+ attachment.value = fs.createReadStream(param.fileName)
+ } else if (param.value) {
+ attachment.value = param.value
+ }
+
+ if (param.fileName) {
+ attachment.options = {
+ filename: param.fileName,
+ contentType: param.contentType ? param.contentType : null
+ }
+ }
+
+ options.formData[param.name] = attachment
+ })
+ break
+
+ default:
+ if (req.postData.text) {
+ options.body = req.postData.text
+ }
+ }
+
+ return options
+}
+
+exports.Har = Har
--- /dev/null
+'use strict'
+
+var jsonSafeStringify = require('json-stringify-safe')
+ , crypto = require('crypto')
+
+function deferMethod() {
+ if(typeof setImmediate === 'undefined') {
+ return process.nextTick
+ }
+
+ return setImmediate
+}
+
+function isFunction(value) {
+ return typeof value === 'function'
+}
+
+function paramsHaveRequestBody(params) {
+ return (
+ params.body ||
+ params.requestBodyStream ||
+ (params.json && typeof params.json !== 'boolean') ||
+ params.multipart
+ )
+}
+
+function safeStringify (obj) {
+ var ret
+ try {
+ ret = JSON.stringify(obj)
+ } catch (e) {
+ ret = jsonSafeStringify(obj)
+ }
+ return ret
+}
+
+function md5 (str) {
+ return crypto.createHash('md5').update(str).digest('hex')
+}
+
+function isReadStream (rs) {
+ return rs.readable && rs.path && rs.mode
+}
+
+function toBase64 (str) {
+ return (new Buffer(str || '', 'utf8')).toString('base64')
+}
+
+exports.isFunction = isFunction
+exports.paramsHaveRequestBody = paramsHaveRequestBody
+exports.safeStringify = safeStringify
+exports.md5 = md5
+exports.isReadStream = isReadStream
+exports.toBase64 = toBase64
+exports.defer = deferMethod()
--- /dev/null
+'use strict'
+
+var uuid = require('node-uuid')
+ , CombinedStream = require('combined-stream')
+ , isstream = require('isstream')
+
+
+function Multipart (request) {
+ this.request = request
+ this.boundary = uuid()
+ this.chunked = false
+ this.body = null
+}
+
+Multipart.prototype.isChunked = function (options) {
+ var self = this
+ , chunked = false
+ , parts = options.data || options
+
+ if (!parts.forEach) {
+ throw new Error('Argument error, options.multipart.')
+ }
+
+ if (options.chunked !== undefined) {
+ chunked = options.chunked
+ }
+
+ if (self.request.getHeader('transfer-encoding') === 'chunked') {
+ chunked = true
+ }
+
+ if (!chunked) {
+ parts.forEach(function (part) {
+ if(typeof part.body === 'undefined') {
+ throw new Error('Body attribute missing in multipart.')
+ }
+ if (isstream(part.body)) {
+ chunked = true
+ }
+ })
+ }
+
+ return chunked
+}
+
+Multipart.prototype.setHeaders = function (chunked) {
+ var self = this
+
+ if (chunked && !self.request.hasHeader('transfer-encoding')) {
+ self.request.setHeader('transfer-encoding', 'chunked')
+ }
+
+ var header = self.request.getHeader('content-type')
+
+ if (!header || header.indexOf('multipart') === -1) {
+ self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary)
+ } else {
+ if (header.indexOf('boundary') !== -1) {
+ self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1')
+ } else {
+ self.request.setHeader('content-type', header + '; boundary=' + self.boundary)
+ }
+ }
+}
+
+Multipart.prototype.build = function (parts, chunked) {
+ var self = this
+ var body = chunked ? new CombinedStream() : []
+
+ function add (part) {
+ return chunked ? body.append(part) : body.push(new Buffer(part))
+ }
+
+ if (self.request.preambleCRLF) {
+ add('\r\n')
+ }
+
+ parts.forEach(function (part) {
+ var preamble = '--' + self.boundary + '\r\n'
+ Object.keys(part).forEach(function (key) {
+ if (key === 'body') { return }
+ preamble += key + ': ' + part[key] + '\r\n'
+ })
+ preamble += '\r\n'
+ add(preamble)
+ add(part.body)
+ add('\r\n')
+ })
+ add('--' + self.boundary + '--')
+
+ if (self.request.postambleCRLF) {
+ add('\r\n')
+ }
+
+ return body
+}
+
+Multipart.prototype.onRequest = function (options) {
+ var self = this
+
+ var chunked = self.isChunked(options)
+ , parts = options.data || options
+
+ self.setHeaders(chunked)
+ self.chunked = chunked
+ self.body = self.build(parts, chunked)
+}
+
+exports.Multipart = Multipart
--- /dev/null
+'use strict'
+
+var qs = require('qs')
+ , caseless = require('caseless')
+ , uuid = require('node-uuid')
+ , oauth = require('oauth-sign')
+
+
+function OAuth (request) {
+ this.request = request
+}
+
+OAuth.prototype.buildParams = function (_oauth, uri, method, query, form, qsLib) {
+ var oa = {}
+ for (var i in _oauth) {
+ oa['oauth_' + i] = _oauth[i]
+ }
+ if (!oa.oauth_version) {
+ oa.oauth_version = '1.0'
+ }
+ if (!oa.oauth_timestamp) {
+ oa.oauth_timestamp = Math.floor( Date.now() / 1000 ).toString()
+ }
+ if (!oa.oauth_nonce) {
+ oa.oauth_nonce = uuid().replace(/-/g, '')
+ }
+ if (!oa.oauth_signature_method) {
+ oa.oauth_signature_method = 'HMAC-SHA1'
+ }
+
+ var consumer_secret_or_private_key = oa.oauth_consumer_secret || oa.oauth_private_key
+ delete oa.oauth_consumer_secret
+ delete oa.oauth_private_key
+
+ var token_secret = oa.oauth_token_secret
+ delete oa.oauth_token_secret
+
+ var realm = oa.oauth_realm
+ delete oa.oauth_realm
+ delete oa.oauth_transport_method
+
+ var baseurl = uri.protocol + '//' + uri.host + uri.pathname
+ var params = qsLib.parse([].concat(query, form, qsLib.stringify(oa)).join('&'))
+
+ oa.oauth_signature = oauth.sign(
+ oa.oauth_signature_method,
+ method,
+ baseurl,
+ params,
+ consumer_secret_or_private_key,
+ token_secret)
+
+ if (realm) {
+ oa.realm = realm
+ }
+
+ return oa
+}
+
+OAuth.prototype.concatParams = function (oa, sep, wrap) {
+ wrap = wrap || ''
+
+ var params = Object.keys(oa).filter(function (i) {
+ return i !== 'realm' && i !== 'oauth_signature'
+ }).sort()
+
+ if (oa.realm) {
+ params.splice(0, 1, 'realm')
+ }
+ params.push('oauth_signature')
+
+ return params.map(function (i) {
+ return i + '=' + wrap + oauth.rfc3986(oa[i]) + wrap
+ }).join(sep)
+}
+
+OAuth.prototype.onRequest = function (_oauth) {
+ var self = this
+ , request = self.request
+
+ var uri = request.uri || {}
+ , method = request.method || ''
+ , headers = caseless(request.headers)
+ , body = request.body || ''
+ , qsLib = request.qsLib || qs
+
+ var form
+ , query
+ , contentType = headers.get('content-type') || ''
+ , formContentType = 'application/x-www-form-urlencoded'
+ , transport = _oauth.transport_method || 'header'
+
+ if (contentType.slice(0, formContentType.length) === formContentType) {
+ contentType = formContentType
+ form = body
+ }
+ if (uri.query) {
+ query = uri.query
+ }
+ if (transport === 'body' && (method !== 'POST' || contentType !== formContentType)) {
+ throw new Error('oauth: transport_method of \'body\' requires \'POST\' ' +
+ 'and content-type \'' + formContentType + '\'')
+ }
+
+ var oa = this.buildParams(_oauth, uri, method, query, form, qsLib)
+
+ switch (transport) {
+ case 'header':
+ request.setHeader('Authorization', 'OAuth ' + this.concatParams(oa, ',', '"'))
+ break
+
+ case 'query':
+ request.path = (query ? '&' : '?') + this.concatParams(oa, '&')
+ break
+
+ case 'body':
+ request.body = (form ? form + '&' : '') + this.concatParams(oa, '&')
+ break
+
+ default:
+ throw new Error('oauth: transport_method invalid')
+ }
+}
+
+exports.OAuth = OAuth
--- /dev/null
+'use strict'
+
+var url = require('url')
+var isUrl = /^https?:/
+
+function Redirect (request) {
+ this.request = request
+ this.followRedirect = true
+ this.followRedirects = true
+ this.followAllRedirects = false
+ this.allowRedirect = function () {return true}
+ this.maxRedirects = 10
+ this.redirects = []
+ this.redirectsFollowed = 0
+ this.removeRefererHeader = false
+}
+
+Redirect.prototype.onRequest = function () {
+ var self = this
+ , request = self.request
+
+ if (request.maxRedirects !== undefined) {
+ self.maxRedirects = request.maxRedirects
+ }
+ if (typeof request.followRedirect === 'function') {
+ self.allowRedirect = request.followRedirect
+ }
+ if (request.followRedirect !== undefined) {
+ self.followRedirects = !!request.followRedirect
+ }
+ if (request.followAllRedirects !== undefined) {
+ self.followAllRedirects = request.followAllRedirects
+ }
+ if (self.followRedirects || self.followAllRedirects) {
+ self.redirects = self.redirects || []
+ }
+ if (request.removeRefererHeader !== undefined) {
+ self.removeRefererHeader = request.removeRefererHeader
+ }
+}
+
+Redirect.prototype.redirectTo = function (response) {
+ var self = this
+ , request = self.request
+
+ var redirectTo = null
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) {
+ var location = response.caseless.get('location')
+ // debug('redirect', location)
+
+ if (self.followAllRedirects) {
+ redirectTo = location
+ } else if (self.followRedirects) {
+ switch (request.method) {
+ case 'PATCH':
+ case 'PUT':
+ case 'POST':
+ case 'DELETE':
+ // Do not follow redirects
+ break
+ default:
+ redirectTo = location
+ break
+ }
+ }
+ } else if (response.statusCode === 401) {
+ var authHeader = request._auth.onResponse(response)
+ if (authHeader) {
+ request.setHeader('authorization', authHeader)
+ redirectTo = request.uri
+ }
+ }
+ return redirectTo
+}
+
+Redirect.prototype.onResponse = function (response) {
+ var self = this
+ , request = self.request
+
+ var redirectTo = self.redirectTo(response)
+ if (!redirectTo || !self.allowRedirect.call(request, response)) {
+ return false
+ }
+
+
+ // debug('redirect to', redirectTo)
+
+ // ignore any potential response body. it cannot possibly be useful
+ // to us at this point.
+ if (request._paused) {
+ response.resume()
+ }
+
+ if (self.redirectsFollowed >= self.maxRedirects) {
+ request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href))
+ return false
+ }
+ self.redirectsFollowed += 1
+
+ if (!isUrl.test(redirectTo)) {
+ redirectTo = url.resolve(request.uri.href, redirectTo)
+ }
+
+ var uriPrev = request.uri
+ request.uri = url.parse(redirectTo)
+
+ // handle the case where we change protocol from https to http or vice versa
+ if (request.uri.protocol !== uriPrev.protocol) {
+ request._updateProtocol()
+ }
+
+ self.redirects.push(
+ { statusCode : response.statusCode
+ , redirectUri: redirectTo
+ }
+ )
+ if (self.followAllRedirects && response.statusCode !== 401 && response.statusCode !== 307) {
+ request.method = 'GET'
+ }
+ // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215
+ delete request.src
+ delete request.req
+ delete request.agent
+ delete request._started
+ if (response.statusCode !== 401 && response.statusCode !== 307) {
+ // Remove parameters from the previous response, unless this is the second request
+ // for a server that requires digest authentication.
+ delete request.body
+ delete request._form
+ if (request.headers) {
+ request.removeHeader('host')
+ request.removeHeader('content-type')
+ request.removeHeader('content-length')
+ if (request.uri.hostname !== request.originalHost.split(':')[0]) {
+ // Remove authorization if changing hostnames (but not if just
+ // changing ports or protocols). This matches the behavior of curl:
+ // https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710
+ request.removeHeader('authorization')
+ }
+ }
+ }
+
+ if (!self.removeRefererHeader) {
+ request.setHeader('referer', request.uri.href)
+ }
+
+ request.emit('redirect')
+
+ request.init()
+
+ return true
+}
+
+exports.Redirect = Redirect
--- /dev/null
+../har-validator/bin/har-validator
\ No newline at end of file
--- /dev/null
+../node-uuid/bin/uuid
\ No newline at end of file
--- /dev/null
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
--- /dev/null
+aws-sign
+========
+
+AWS signing. Originally pulled from LearnBoost/knox, maintained as vendor in request, now a standalone module.
--- /dev/null
+
+/*!
+ * knox - auth
+ * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var crypto = require('crypto')
+ , parse = require('url').parse
+ ;
+
+/**
+ * Valid keys.
+ */
+
+var keys =
+ [ 'acl'
+ , 'location'
+ , 'logging'
+ , 'notification'
+ , 'partNumber'
+ , 'policy'
+ , 'requestPayment'
+ , 'torrent'
+ , 'uploadId'
+ , 'uploads'
+ , 'versionId'
+ , 'versioning'
+ , 'versions'
+ , 'website'
+ ]
+
+/**
+ * Return an "Authorization" header value with the given `options`
+ * in the form of "AWS <key>:<signature>"
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+function authorization (options) {
+ return 'AWS ' + options.key + ':' + sign(options)
+}
+
+module.exports = authorization
+module.exports.authorization = authorization
+
+/**
+ * Simple HMAC-SHA1 Wrapper
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+function hmacSha1 (options) {
+ return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64')
+}
+
+module.exports.hmacSha1 = hmacSha1
+
+/**
+ * Create a base64 sha1 HMAC for `options`.
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+function sign (options) {
+ options.message = stringToSign(options)
+ return hmacSha1(options)
+}
+module.exports.sign = sign
+
+/**
+ * Create a base64 sha1 HMAC for `options`.
+ *
+ * Specifically to be used with S3 presigned URLs
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+function signQuery (options) {
+ options.message = queryStringToSign(options)
+ return hmacSha1(options)
+}
+module.exports.signQuery= signQuery
+
+/**
+ * Return a string for sign() with the given `options`.
+ *
+ * Spec:
+ *
+ * <verb>\n
+ * <md5>\n
+ * <content-type>\n
+ * <date>\n
+ * [headers\n]
+ * <resource>
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+function stringToSign (options) {
+ var headers = options.amazonHeaders || ''
+ if (headers) headers += '\n'
+ var r =
+ [ options.verb
+ , options.md5
+ , options.contentType
+ , options.date ? options.date.toUTCString() : ''
+ , headers + options.resource
+ ]
+ return r.join('\n')
+}
+module.exports.queryStringToSign = stringToSign
+
+/**
+ * Return a string for sign() with the given `options`, but is meant exclusively
+ * for S3 presigned URLs
+ *
+ * Spec:
+ *
+ * <date>\n
+ * <resource>
+ *
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+
+function queryStringToSign (options){
+ return 'GET\n\n\n' + options.date + '\n' + options.resource
+}
+module.exports.queryStringToSign = queryStringToSign
+
+/**
+ * Perform the following:
+ *
+ * - ignore non-amazon headers
+ * - lowercase fields
+ * - sort lexicographically
+ * - trim whitespace between ":"
+ * - join with newline
+ *
+ * @param {Object} headers
+ * @return {String}
+ * @api private
+ */
+
+function canonicalizeHeaders (headers) {
+ var buf = []
+ , fields = Object.keys(headers)
+ ;
+ for (var i = 0, len = fields.length; i < len; ++i) {
+ var field = fields[i]
+ , val = headers[field]
+ , field = field.toLowerCase()
+ ;
+ if (0 !== field.indexOf('x-amz')) continue
+ buf.push(field + ':' + val)
+ }
+ return buf.sort().join('\n')
+}
+module.exports.canonicalizeHeaders = canonicalizeHeaders
+
+/**
+ * Perform the following:
+ *
+ * - ignore non sub-resources
+ * - sort lexicographically
+ *
+ * @param {String} resource
+ * @return {String}
+ * @api private
+ */
+
+function canonicalizeResource (resource) {
+ var url = parse(resource, true)
+ , path = url.pathname
+ , buf = []
+ ;
+
+ Object.keys(url.query).forEach(function(key){
+ if (!~keys.indexOf(key)) return
+ var val = '' == url.query[key] ? '' : '=' + encodeURIComponent(url.query[key])
+ buf.push(key + val)
+ })
+
+ return path + (buf.length ? '?' + buf.sort().join('&') : '')
+}
+module.exports.canonicalizeResource = canonicalizeResource
--- /dev/null
+{
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com",
+ "url": "http://www.futurealoof.com"
+ },
+ "name": "aws-sign2",
+ "description": "AWS signing. Originally pulled from LearnBoost/knox, maintained as vendor in request, now a standalone module.",
+ "version": "0.5.0",
+ "repository": {
+ "url": "git+https://github.com/mikeal/aws-sign.git"
+ },
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "readme": "aws-sign\n========\n\nAWS signing. Originally pulled from LearnBoost/knox, maintained as vendor in request, now a standalone module.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/mikeal/aws-sign/issues"
+ },
+ "_id": "aws-sign2@0.5.0",
+ "dist": {
+ "shasum": "c57103f7a17fc037f02d7c2e64b602ea223f7d63",
+ "tarball": "http://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
+ },
+ "_from": "aws-sign2@>=0.5.0 <0.6.0",
+ "_npmVersion": "1.3.2",
+ "_npmUser": {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "c57103f7a17fc037f02d7c2e64b602ea223f7d63",
+ "_resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz",
+ "homepage": "https://github.com/mikeal/aws-sign",
+ "scripts": {}
+}
--- /dev/null
+{
+ "predef": [ ]
+ , "bitwise": false
+ , "camelcase": false
+ , "curly": false
+ , "eqeqeq": false
+ , "forin": false
+ , "immed": false
+ , "latedef": false
+ , "noarg": true
+ , "noempty": true
+ , "nonew": true
+ , "plusplus": false
+ , "quotmark": true
+ , "regexp": false
+ , "undef": true
+ , "unused": true
+ , "strict": false
+ , "trailing": true
+ , "maxlen": 120
+ , "asi": true
+ , "boss": true
+ , "debug": true
+ , "eqnull": true
+ , "esnext": true
+ , "evil": true
+ , "expr": true
+ , "funcscope": false
+ , "globalstrict": false
+ , "iterator": false
+ , "lastsemic": true
+ , "laxbreak": true
+ , "laxcomma": true
+ , "loopfunc": true
+ , "multistr": false
+ , "onecase": false
+ , "proto": false
+ , "regexdash": false
+ , "scripturl": true
+ , "smarttabs": false
+ , "shadow": false
+ , "sub": true
+ , "supernew": false
+ , "validthis": true
+ , "browser": true
+ , "couch": false
+ , "devel": false
+ , "dojo": false
+ , "mootools": false
+ , "node": true
+ , "nonstandard": true
+ , "prototypejs": false
+ , "rhino": false
+ , "worker": true
+ , "wsh": false
+ , "nomen": false
+ , "onevar": false
+ , "passfail": false
+}
\ No newline at end of file
--- /dev/null
+node_modules/
\ No newline at end of file
--- /dev/null
+language: node_js
+node_js:
+ - 0.8
+ - "0.10"
+branches:
+ only:
+ - master
+notifications:
+ email:
+ - rod@vagg.org
+script: npm test
\ No newline at end of file
--- /dev/null
+The MIT License (MIT)
+=====================
+
+Copyright (c) 2014 bl contributors
+----------------------------------
+
+*bl contributors listed at <https://github.com/rvagg/bl#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.
--- /dev/null
+# bl *(BufferList)*
+
+**A Node.js Buffer list collector, reader and streamer thingy.**
+
+[](https://nodei.co/npm/bl/)
+[](https://nodei.co/npm/bl/)
+
+**bl** is a storage object for collections of Node Buffers, exposing them with the main Buffer readable API. Also works as a duplex stream so you can collect buffers from a stream that emits them and emit buffers to a stream that consumes them!
+
+The original buffers are kept intact and copies are only done as necessary. Any reads that require the use of a single original buffer will return a slice of that buffer only (which references the same memory as the original buffer). Reads that span buffers perform concatenation as required and return the results transparently.
+
+```js
+const BufferList = require('bl')
+
+var bl = new BufferList()
+bl.append(new Buffer('abcd'))
+bl.append(new Buffer('efg'))
+bl.append('hi') // bl will also accept & convert Strings
+bl.append(new Buffer('j'))
+bl.append(new Buffer([ 0x3, 0x4 ]))
+
+console.log(bl.length) // 12
+
+console.log(bl.slice(0, 10).toString('ascii')) // 'abcdefghij'
+console.log(bl.slice(3, 10).toString('ascii')) // 'defghij'
+console.log(bl.slice(3, 6).toString('ascii')) // 'def'
+console.log(bl.slice(3, 8).toString('ascii')) // 'defgh'
+console.log(bl.slice(5, 10).toString('ascii')) // 'fghij'
+
+// or just use toString!
+console.log(bl.toString()) // 'abcdefghij\u0003\u0004'
+console.log(bl.toString('ascii', 3, 8)) // 'defgh'
+console.log(bl.toString('ascii', 5, 10)) // 'fghij'
+
+// other standard Buffer readables
+console.log(bl.readUInt16BE(10)) // 0x0304
+console.log(bl.readUInt16LE(10)) // 0x0403
+```
+
+Give it a callback in the constructor and use it just like **[concat-stream](https://github.com/maxogden/node-concat-stream)**:
+
+```js
+const bl = require('bl')
+ , fs = require('fs')
+
+fs.createReadStream('README.md')
+ .pipe(bl(function (err, data) { // note 'new' isn't strictly required
+ // `data` is a complete Buffer object containing the full data
+ console.log(data.toString())
+ }))
+```
+
+Note that when you use the *callback* method like this, the resulting `data` parameter is a concatenation of all `Buffer` objects in the list. If you want to avoid the overhead of this concatenation (in cases of extreme performance consciousness), then avoid the *callback* method and just listen to `'end'` instead, like a standard Stream.
+
+Or to fetch a URL using [hyperquest](https://github.com/substack/hyperquest) (should work with [request](http://github.com/mikeal/request) and even plain Node http too!):
+```js
+const hyperquest = require('hyperquest')
+ , bl = require('bl')
+ , url = 'https://raw.github.com/rvagg/bl/master/README.md'
+
+hyperquest(url).pipe(bl(function (err, data) {
+ console.log(data.toString())
+}))
+```
+
+Or, use it as a readable stream to recompose a list of Buffers to an output source:
+
+```js
+const BufferList = require('bl')
+ , fs = require('fs')
+
+var bl = new BufferList()
+bl.append(new Buffer('abcd'))
+bl.append(new Buffer('efg'))
+bl.append(new Buffer('hi'))
+bl.append(new Buffer('j'))
+
+bl.pipe(fs.createWriteStream('gibberish.txt'))
+```
+
+## API
+
+ * <a href="#ctor"><code><b>new BufferList([ callback ])</b></code></a>
+ * <a href="#length"><code>bl.<b>length</b></code></a>
+ * <a href="#append"><code>bl.<b>append(buffer)</b></code></a>
+ * <a href="#get"><code>bl.<b>get(index)</b></code></a>
+ * <a href="#slice"><code>bl.<b>slice([ start[, end ] ])</b></code></a>
+ * <a href="#copy"><code>bl.<b>copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])</b></code></a>
+ * <a href="#duplicate"><code>bl.<b>duplicate()</b></code></a>
+ * <a href="#consume"><code>bl.<b>consume(bytes)</b></code></a>
+ * <a href="#toString"><code>bl.<b>toString([encoding, [ start, [ end ]]])</b></code></a>
+ * <a href="#readXX"><code>bl.<b>readDoubleBE()</b></code>, <code>bl.<b>readDoubleLE()</b></code>, <code>bl.<b>readFloatBE()</b></code>, <code>bl.<b>readFloatLE()</b></code>, <code>bl.<b>readInt32BE()</b></code>, <code>bl.<b>readInt32LE()</b></code>, <code>bl.<b>readUInt32BE()</b></code>, <code>bl.<b>readUInt32LE()</b></code>, <code>bl.<b>readInt16BE()</b></code>, <code>bl.<b>readInt16LE()</b></code>, <code>bl.<b>readUInt16BE()</b></code>, <code>bl.<b>readUInt16LE()</b></code>, <code>bl.<b>readInt8()</b></code>, <code>bl.<b>readUInt8()</b></code></a>
+ * <a href="#streams">Streams</a>
+
+--------------------------------------------------------
+<a name="ctor"></a>
+### new BufferList([ callback | buffer | buffer array ])
+The constructor takes an optional callback, if supplied, the callback will be called with an error argument followed by a reference to the **bl** instance, when `bl.end()` is called (i.e. from a piped stream). This is a convenient method of collecting the entire contents of a stream, particularly when the stream is *chunky*, such as a network stream.
+
+Normally, no arguments are required for the constructor, but you can initialise the list by passing in a single `Buffer` object or an array of `Buffer` object.
+
+`new` is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:
+
+```js
+var bl = require('bl')
+var myinstance = bl()
+
+// equivilant to:
+
+var BufferList = require('bl')
+var myinstance = new BufferList()
+```
+
+--------------------------------------------------------
+<a name="length"></a>
+### bl.length
+Get the length of the list in bytes. This is the sum of the lengths of all of the buffers contained in the list, minus any initial offset for a semi-consumed buffer at the beginning. Should accurately represent the total number of bytes that can be read from the list.
+
+--------------------------------------------------------
+<a name="append"></a>
+### bl.append(buffer)
+`append(buffer)` adds an additional buffer or BufferList to the internal list.
+
+--------------------------------------------------------
+<a name="get"></a>
+### bl.get(index)
+`get()` will return the byte at the specified index.
+
+--------------------------------------------------------
+<a name="slice"></a>
+### bl.slice([ start, [ end ] ])
+`slice()` returns a new `Buffer` object containing the bytes within the range specified. Both `start` and `end` are optional and will default to the beginning and end of the list respectively.
+
+If the requested range spans a single internal buffer then a slice of that buffer will be returned which shares the original memory range of that Buffer. If the range spans multiple buffers then copy operations will likely occur to give you a uniform Buffer.
+
+--------------------------------------------------------
+<a name="copy"></a>
+### bl.copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])
+`copy()` copies the content of the list in the `dest` buffer, starting from `destStart` and containing the bytes within the range specified with `srcStart` to `srcEnd`. `destStart`, `start` and `end` are optional and will default to the beginning of the `dest` buffer, and the beginning and end of the list respectively.
+
+--------------------------------------------------------
+<a name="duplicate"></a>
+### bl.duplicate()
+`duplicate()` performs a **shallow-copy** of the list. The internal Buffers remains the same, so if you change the underlying Buffers, the change will be reflected in both the original and the duplicate. This method is needed if you want to call `consume()` or `pipe()` and still keep the original list.Example:
+
+```js
+var bl = new BufferList()
+
+bl.append('hello')
+bl.append(' world')
+bl.append('\n')
+
+bl.duplicate().pipe(process.stdout, { end: false })
+
+console.log(bl.toString())
+```
+
+--------------------------------------------------------
+<a name="consume"></a>
+### bl.consume(bytes)
+`consume()` will shift bytes *off the start of the list*. The number of bytes consumed don't need to line up with the sizes of the internal Buffers—initial offsets will be calculated accordingly in order to give you a consistent view of the data.
+
+--------------------------------------------------------
+<a name="toString"></a>
+### bl.toString([encoding, [ start, [ end ]]])
+`toString()` will return a string representation of the buffer. The optional `start` and `end` arguments are passed on to `slice()`, while the `encoding` is passed on to `toString()` of the resulting Buffer. See the [Buffer#toString()](http://nodejs.org/docs/latest/api/buffer.html#buffer_buf_tostring_encoding_start_end) documentation for more information.
+
+--------------------------------------------------------
+<a name="readXX"></a>
+### bl.readDoubleBE(), bl.readDoubleLE(), bl.readFloatBE(), bl.readFloatLE(), bl.readInt32BE(), bl.readInt32LE(), bl.readUInt32BE(), bl.readUInt32LE(), bl.readInt16BE(), bl.readInt16LE(), bl.readUInt16BE(), bl.readUInt16LE(), bl.readInt8(), bl.readUInt8()
+
+All of the standard byte-reading methods of the `Buffer` interface are implemented and will operate across internal Buffer boundaries transparently.
+
+See the <b><code>[Buffer](http://nodejs.org/docs/latest/api/buffer.html)</code></b> documentation for how these work.
+
+--------------------------------------------------------
+<a name="streams"></a>
+### Streams
+**bl** is a Node **[Duplex Stream](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_duplex)**, so it can be read from and written to like a standard Node stream. You can also `pipe()` to and from a **bl** instance.
+
+--------------------------------------------------------
+
+## Contributors
+
+**bl** is brought to you by the following hackers:
+
+ * [Rod Vagg](https://github.com/rvagg)
+ * [Matteo Collina](https://github.com/mcollina)
+ * [Jarett Cruger](https://github.com/jcrugzz)
+
+=======
+
+<a name="license"></a>
+## License & copyright
+
+Copyright (c) 2013-2014 bl contributors (listed above).
+
+bl is licensed under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.
--- /dev/null
+var DuplexStream = require('readable-stream/duplex')
+ , util = require('util')
+
+function BufferList (callback) {
+ if (!(this instanceof BufferList))
+ return new BufferList(callback)
+
+ this._bufs = []
+ this.length = 0
+
+ if (typeof callback == 'function') {
+ this._callback = callback
+
+ var piper = function (err) {
+ if (this._callback) {
+ this._callback(err)
+ this._callback = null
+ }
+ }.bind(this)
+
+ this.on('pipe', function (src) {
+ src.on('error', piper)
+ })
+ this.on('unpipe', function (src) {
+ src.removeListener('error', piper)
+ })
+ }
+ else if (Buffer.isBuffer(callback))
+ this.append(callback)
+ else if (Array.isArray(callback)) {
+ callback.forEach(function (b) {
+ Buffer.isBuffer(b) && this.append(b)
+ }.bind(this))
+ }
+
+ DuplexStream.call(this)
+}
+
+util.inherits(BufferList, DuplexStream)
+
+BufferList.prototype._offset = function (offset) {
+ var tot = 0, i = 0, _t
+ for (; i < this._bufs.length; i++) {
+ _t = tot + this._bufs[i].length
+ if (offset < _t)
+ return [ i, offset - tot ]
+ tot = _t
+ }
+}
+
+BufferList.prototype.append = function (buf) {
+ var isBuffer = Buffer.isBuffer(buf) ||
+ buf instanceof BufferList
+
+ this._bufs.push(isBuffer ? buf : new Buffer(buf))
+ this.length += buf.length
+ return this
+}
+
+BufferList.prototype._write = function (buf, encoding, callback) {
+ this.append(buf)
+ if (callback)
+ callback()
+}
+
+BufferList.prototype._read = function (size) {
+ if (!this.length)
+ return this.push(null)
+ size = Math.min(size, this.length)
+ this.push(this.slice(0, size))
+ this.consume(size)
+}
+
+BufferList.prototype.end = function (chunk) {
+ DuplexStream.prototype.end.call(this, chunk)
+
+ if (this._callback) {
+ this._callback(null, this.slice())
+ this._callback = null
+ }
+}
+
+BufferList.prototype.get = function (index) {
+ return this.slice(index, index + 1)[0]
+}
+
+BufferList.prototype.slice = function (start, end) {
+ return this.copy(null, 0, start, end)
+}
+
+BufferList.prototype.copy = function (dst, dstStart, srcStart, srcEnd) {
+ if (typeof srcStart != 'number' || srcStart < 0)
+ srcStart = 0
+ if (typeof srcEnd != 'number' || srcEnd > this.length)
+ srcEnd = this.length
+ if (srcStart >= this.length)
+ return dst || new Buffer(0)
+ if (srcEnd <= 0)
+ return dst || new Buffer(0)
+
+ var copy = !!dst
+ , off = this._offset(srcStart)
+ , len = srcEnd - srcStart
+ , bytes = len
+ , bufoff = (copy && dstStart) || 0
+ , start = off[1]
+ , l
+ , i
+
+ // copy/slice everything
+ if (srcStart === 0 && srcEnd == this.length) {
+ if (!copy) // slice, just return a full concat
+ return Buffer.concat(this._bufs)
+
+ // copy, need to copy individual buffers
+ for (i = 0; i < this._bufs.length; i++) {
+ this._bufs[i].copy(dst, bufoff)
+ bufoff += this._bufs[i].length
+ }
+
+ return dst
+ }
+
+ // easy, cheap case where it's a subset of one of the buffers
+ if (bytes <= this._bufs[off[0]].length - start) {
+ return copy
+ ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes)
+ : this._bufs[off[0]].slice(start, start + bytes)
+ }
+
+ if (!copy) // a slice, we need something to copy in to
+ dst = new Buffer(len)
+
+ for (i = off[0]; i < this._bufs.length; i++) {
+ l = this._bufs[i].length - start
+
+ if (bytes > l) {
+ this._bufs[i].copy(dst, bufoff, start)
+ } else {
+ this._bufs[i].copy(dst, bufoff, start, start + bytes)
+ break
+ }
+
+ bufoff += l
+ bytes -= l
+
+ if (start)
+ start = 0
+ }
+
+ return dst
+}
+
+BufferList.prototype.toString = function (encoding, start, end) {
+ return this.slice(start, end).toString(encoding)
+}
+
+BufferList.prototype.consume = function (bytes) {
+ while (this._bufs.length) {
+ if (bytes > this._bufs[0].length) {
+ bytes -= this._bufs[0].length
+ this.length -= this._bufs[0].length
+ this._bufs.shift()
+ } else {
+ this._bufs[0] = this._bufs[0].slice(bytes)
+ this.length -= bytes
+ break
+ }
+ }
+ return this
+}
+
+BufferList.prototype.duplicate = function () {
+ var i = 0
+ , copy = new BufferList()
+
+ for (; i < this._bufs.length; i++)
+ copy.append(this._bufs[i])
+
+ return copy
+}
+
+BufferList.prototype.destroy = function () {
+ this._bufs.length = 0;
+ this.length = 0;
+ this.push(null);
+}
+
+;(function () {
+ var methods = {
+ 'readDoubleBE' : 8
+ , 'readDoubleLE' : 8
+ , 'readFloatBE' : 4
+ , 'readFloatLE' : 4
+ , 'readInt32BE' : 4
+ , 'readInt32LE' : 4
+ , 'readUInt32BE' : 4
+ , 'readUInt32LE' : 4
+ , 'readInt16BE' : 2
+ , 'readInt16LE' : 2
+ , 'readUInt16BE' : 2
+ , 'readUInt16LE' : 2
+ , 'readInt8' : 1
+ , 'readUInt8' : 1
+ }
+
+ for (var m in methods) {
+ (function (m) {
+ BufferList.prototype[m] = function (offset) {
+ return this.slice(offset, offset + methods[m])[m](0)
+ }
+ }(m))
+ }
+}())
+
+module.exports = BufferList
--- /dev/null
+build/
+test/
+examples/
+fs.js
+zlib.js
\ No newline at end of file
--- /dev/null
+Copyright Joyent, Inc. and other Node contributors. 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.
--- /dev/null
+# readable-stream
+
+***Node-core streams for userland***
+
+[](https://nodei.co/npm/readable-stream/)
+[](https://nodei.co/npm/readable-stream/)
+
+This package is a mirror of the Streams2 and Streams3 implementations in Node-core.
+
+If you want to guarantee a stable streams base, regardless of what version of Node you, or the users of your libraries are using, use **readable-stream** *only* and avoid the *"stream"* module in Node-core.
+
+**readable-stream** comes in two major versions, v1.0.x and v1.1.x. The former tracks the Streams2 implementation in Node 0.10, including bug-fixes and minor improvements as they are added. The latter tracks Streams3 as it develops in Node 0.11; we will likely see a v1.2.x branch for Node 0.12.
+
+**readable-stream** uses proper patch-level versioning so if you pin to `"~1.0.0"` you’ll get the latest Node 0.10 Streams2 implementation, including any fixes and minor non-breaking improvements. The patch-level versions of 1.0.x and 1.1.x should mirror the patch-level versions of Node-core releases. You should prefer the **1.0.x** releases for now and when you’re ready to start using Streams3, pin to `"~1.1.0"`
+
--- /dev/null
+module.exports = require("./lib/_stream_duplex.js")
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+// a duplex stream is just a stream that is both readable and writable.
+// Since JS doesn't have multiple prototypal inheritance, this class
+// prototypally inherits from Readable, and then parasitically from
+// Writable.
+
+module.exports = Duplex;
+
+/*<replacement>*/
+var objectKeys = Object.keys || function (obj) {
+ var keys = [];
+ for (var key in obj) keys.push(key);
+ return keys;
+}
+/*</replacement>*/
+
+
+/*<replacement>*/
+var util = require('core-util-is');
+util.inherits = require('inherits');
+/*</replacement>*/
+
+var Readable = require('./_stream_readable');
+var Writable = require('./_stream_writable');
+
+util.inherits(Duplex, Readable);
+
+forEach(objectKeys(Writable.prototype), function(method) {
+ if (!Duplex.prototype[method])
+ Duplex.prototype[method] = Writable.prototype[method];
+});
+
+function Duplex(options) {
+ if (!(this instanceof Duplex))
+ return new Duplex(options);
+
+ Readable.call(this, options);
+ Writable.call(this, options);
+
+ if (options && options.readable === false)
+ this.readable = false;
+
+ if (options && options.writable === false)
+ this.writable = false;
+
+ this.allowHalfOpen = true;
+ if (options && options.allowHalfOpen === false)
+ this.allowHalfOpen = false;
+
+ this.once('end', onend);
+}
+
+// the no-half-open enforcer
+function onend() {
+ // if we allow half-open state, or if the writable side ended,
+ // then we're ok.
+ if (this.allowHalfOpen || this._writableState.ended)
+ return;
+
+ // no more data can be written.
+ // But allow more writes to happen in this tick.
+ process.nextTick(this.end.bind(this));
+}
+
+function forEach (xs, f) {
+ for (var i = 0, l = xs.length; i < l; i++) {
+ f(xs[i], i);
+ }
+}
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+// a passthrough stream.
+// basically just the most minimal sort of Transform stream.
+// Every written chunk gets output as-is.
+
+module.exports = PassThrough;
+
+var Transform = require('./_stream_transform');
+
+/*<replacement>*/
+var util = require('core-util-is');
+util.inherits = require('inherits');
+/*</replacement>*/
+
+util.inherits(PassThrough, Transform);
+
+function PassThrough(options) {
+ if (!(this instanceof PassThrough))
+ return new PassThrough(options);
+
+ Transform.call(this, options);
+}
+
+PassThrough.prototype._transform = function(chunk, encoding, cb) {
+ cb(null, chunk);
+};
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+module.exports = Readable;
+
+/*<replacement>*/
+var isArray = require('isarray');
+/*</replacement>*/
+
+
+/*<replacement>*/
+var Buffer = require('buffer').Buffer;
+/*</replacement>*/
+
+Readable.ReadableState = ReadableState;
+
+var EE = require('events').EventEmitter;
+
+/*<replacement>*/
+if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
+ return emitter.listeners(type).length;
+};
+/*</replacement>*/
+
+var Stream = require('stream');
+
+/*<replacement>*/
+var util = require('core-util-is');
+util.inherits = require('inherits');
+/*</replacement>*/
+
+var StringDecoder;
+
+util.inherits(Readable, Stream);
+
+function ReadableState(options, stream) {
+ options = options || {};
+
+ // the point at which it stops calling _read() to fill the buffer
+ // Note: 0 is a valid value, means "don't call _read preemptively ever"
+ var hwm = options.highWaterMark;
+ this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
+
+ // cast to ints.
+ this.highWaterMark = ~~this.highWaterMark;
+
+ this.buffer = [];
+ this.length = 0;
+ this.pipes = null;
+ this.pipesCount = 0;
+ this.flowing = false;
+ this.ended = false;
+ this.endEmitted = false;
+ this.reading = false;
+
+ // In streams that never have any data, and do push(null) right away,
+ // the consumer can miss the 'end' event if they do some I/O before
+ // consuming the stream. So, we don't emit('end') until some reading
+ // happens.
+ this.calledRead = false;
+
+ // a flag to be able to tell if the onwrite cb is called immediately,
+ // or on a later tick. We set this to true at first, becuase any
+ // actions that shouldn't happen until "later" should generally also
+ // not happen before the first write call.
+ this.sync = true;
+
+ // whenever we return null, then we set a flag to say
+ // that we're awaiting a 'readable' event emission.
+ this.needReadable = false;
+ this.emittedReadable = false;
+ this.readableListening = false;
+
+
+ // object stream flag. Used to make read(n) ignore n and to
+ // make all the buffer merging and length checks go away
+ this.objectMode = !!options.objectMode;
+
+ // Crypto is kind of old and crusty. Historically, its default string
+ // encoding is 'binary' so we have to make this configurable.
+ // Everything else in the universe uses 'utf8', though.
+ this.defaultEncoding = options.defaultEncoding || 'utf8';
+
+ // when piping, we only care about 'readable' events that happen
+ // after read()ing all the bytes and not getting any pushback.
+ this.ranOut = false;
+
+ // the number of writers that are awaiting a drain event in .pipe()s
+ this.awaitDrain = 0;
+
+ // if true, a maybeReadMore has been scheduled
+ this.readingMore = false;
+
+ this.decoder = null;
+ this.encoding = null;
+ if (options.encoding) {
+ if (!StringDecoder)
+ StringDecoder = require('string_decoder/').StringDecoder;
+ this.decoder = new StringDecoder(options.encoding);
+ this.encoding = options.encoding;
+ }
+}
+
+function Readable(options) {
+ if (!(this instanceof Readable))
+ return new Readable(options);
+
+ this._readableState = new ReadableState(options, this);
+
+ // legacy
+ this.readable = true;
+
+ Stream.call(this);
+}
+
+// Manually shove something into the read() buffer.
+// This returns true if the highWaterMark has not been hit yet,
+// similar to how Writable.write() returns true if you should
+// write() some more.
+Readable.prototype.push = function(chunk, encoding) {
+ var state = this._readableState;
+
+ if (typeof chunk === 'string' && !state.objectMode) {
+ encoding = encoding || state.defaultEncoding;
+ if (encoding !== state.encoding) {
+ chunk = new Buffer(chunk, encoding);
+ encoding = '';
+ }
+ }
+
+ return readableAddChunk(this, state, chunk, encoding, false);
+};
+
+// Unshift should *always* be something directly out of read()
+Readable.prototype.unshift = function(chunk) {
+ var state = this._readableState;
+ return readableAddChunk(this, state, chunk, '', true);
+};
+
+function readableAddChunk(stream, state, chunk, encoding, addToFront) {
+ var er = chunkInvalid(state, chunk);
+ if (er) {
+ stream.emit('error', er);
+ } else if (chunk === null || chunk === undefined) {
+ state.reading = false;
+ if (!state.ended)
+ onEofChunk(stream, state);
+ } else if (state.objectMode || chunk && chunk.length > 0) {
+ if (state.ended && !addToFront) {
+ var e = new Error('stream.push() after EOF');
+ stream.emit('error', e);
+ } else if (state.endEmitted && addToFront) {
+ var e = new Error('stream.unshift() after end event');
+ stream.emit('error', e);
+ } else {
+ if (state.decoder && !addToFront && !encoding)
+ chunk = state.decoder.write(chunk);
+
+ // update the buffer info.
+ state.length += state.objectMode ? 1 : chunk.length;
+ if (addToFront) {
+ state.buffer.unshift(chunk);
+ } else {
+ state.reading = false;
+ state.buffer.push(chunk);
+ }
+
+ if (state.needReadable)
+ emitReadable(stream);
+
+ maybeReadMore(stream, state);
+ }
+ } else if (!addToFront) {
+ state.reading = false;
+ }
+
+ return needMoreData(state);
+}
+
+
+
+// if it's past the high water mark, we can push in some more.
+// Also, if we have no data yet, we can stand some
+// more bytes. This is to work around cases where hwm=0,
+// such as the repl. Also, if the push() triggered a
+// readable event, and the user called read(largeNumber) such that
+// needReadable was set, then we ought to push more, so that another
+// 'readable' event will be triggered.
+function needMoreData(state) {
+ return !state.ended &&
+ (state.needReadable ||
+ state.length < state.highWaterMark ||
+ state.length === 0);
+}
+
+// backwards compatibility.
+Readable.prototype.setEncoding = function(enc) {
+ if (!StringDecoder)
+ StringDecoder = require('string_decoder/').StringDecoder;
+ this._readableState.decoder = new StringDecoder(enc);
+ this._readableState.encoding = enc;
+};
+
+// Don't raise the hwm > 128MB
+var MAX_HWM = 0x800000;
+function roundUpToNextPowerOf2(n) {
+ if (n >= MAX_HWM) {
+ n = MAX_HWM;
+ } else {
+ // Get the next highest power of 2
+ n--;
+ for (var p = 1; p < 32; p <<= 1) n |= n >> p;
+ n++;
+ }
+ return n;
+}
+
+function howMuchToRead(n, state) {
+ if (state.length === 0 && state.ended)
+ return 0;
+
+ if (state.objectMode)
+ return n === 0 ? 0 : 1;
+
+ if (n === null || isNaN(n)) {
+ // only flow one buffer at a time
+ if (state.flowing && state.buffer.length)
+ return state.buffer[0].length;
+ else
+ return state.length;
+ }
+
+ if (n <= 0)
+ return 0;
+
+ // If we're asking for more than the target buffer level,
+ // then raise the water mark. Bump up to the next highest
+ // power of 2, to prevent increasing it excessively in tiny
+ // amounts.
+ if (n > state.highWaterMark)
+ state.highWaterMark = roundUpToNextPowerOf2(n);
+
+ // don't have that much. return null, unless we've ended.
+ if (n > state.length) {
+ if (!state.ended) {
+ state.needReadable = true;
+ return 0;
+ } else
+ return state.length;
+ }
+
+ return n;
+}
+
+// you can override either this method, or the async _read(n) below.
+Readable.prototype.read = function(n) {
+ var state = this._readableState;
+ state.calledRead = true;
+ var nOrig = n;
+ var ret;
+
+ if (typeof n !== 'number' || n > 0)
+ state.emittedReadable = false;
+
+ // if we're doing read(0) to trigger a readable event, but we
+ // already have a bunch of data in the buffer, then just trigger
+ // the 'readable' event and move on.
+ if (n === 0 &&
+ state.needReadable &&
+ (state.length >= state.highWaterMark || state.ended)) {
+ emitReadable(this);
+ return null;
+ }
+
+ n = howMuchToRead(n, state);
+
+ // if we've ended, and we're now clear, then finish it up.
+ if (n === 0 && state.ended) {
+ ret = null;
+
+ // In cases where the decoder did not receive enough data
+ // to produce a full chunk, then immediately received an
+ // EOF, state.buffer will contain [<Buffer >, <Buffer 00 ...>].
+ // howMuchToRead will see this and coerce the amount to
+ // read to zero (because it's looking at the length of the
+ // first <Buffer > in state.buffer), and we'll end up here.
+ //
+ // This can only happen via state.decoder -- no other venue
+ // exists for pushing a zero-length chunk into state.buffer
+ // and triggering this behavior. In this case, we return our
+ // remaining data and end the stream, if appropriate.
+ if (state.length > 0 && state.decoder) {
+ ret = fromList(n, state);
+ state.length -= ret.length;
+ }
+
+ if (state.length === 0)
+ endReadable(this);
+
+ return ret;
+ }
+
+ // All the actual chunk generation logic needs to be
+ // *below* the call to _read. The reason is that in certain
+ // synthetic stream cases, such as passthrough streams, _read
+ // may be a completely synchronous operation which may change
+ // the state of the read buffer, providing enough data when
+ // before there was *not* enough.
+ //
+ // So, the steps are:
+ // 1. Figure out what the state of things will be after we do
+ // a read from the buffer.
+ //
+ // 2. If that resulting state will trigger a _read, then call _read.
+ // Note that this may be asynchronous, or synchronous. Yes, it is
+ // deeply ugly to write APIs this way, but that still doesn't mean
+ // that the Readable class should behave improperly, as streams are
+ // designed to be sync/async agnostic.
+ // Take note if the _read call is sync or async (ie, if the read call
+ // has returned yet), so that we know whether or not it's safe to emit
+ // 'readable' etc.
+ //
+ // 3. Actually pull the requested chunks out of the buffer and return.
+
+ // if we need a readable event, then we need to do some reading.
+ var doRead = state.needReadable;
+
+ // if we currently have less than the highWaterMark, then also read some
+ if (state.length - n <= state.highWaterMark)
+ doRead = true;
+
+ // however, if we've ended, then there's no point, and if we're already
+ // reading, then it's unnecessary.
+ if (state.ended || state.reading)
+ doRead = false;
+
+ if (doRead) {
+ state.reading = true;
+ state.sync = true;
+ // if the length is currently zero, then we *need* a readable event.
+ if (state.length === 0)
+ state.needReadable = true;
+ // call internal read method
+ this._read(state.highWaterMark);
+ state.sync = false;
+ }
+
+ // If _read called its callback synchronously, then `reading`
+ // will be false, and we need to re-evaluate how much data we
+ // can return to the user.
+ if (doRead && !state.reading)
+ n = howMuchToRead(nOrig, state);
+
+ if (n > 0)
+ ret = fromList(n, state);
+ else
+ ret = null;
+
+ if (ret === null) {
+ state.needReadable = true;
+ n = 0;
+ }
+
+ state.length -= n;
+
+ // If we have nothing in the buffer, then we want to know
+ // as soon as we *do* get something into the buffer.
+ if (state.length === 0 && !state.ended)
+ state.needReadable = true;
+
+ // If we happened to read() exactly the remaining amount in the
+ // buffer, and the EOF has been seen at this point, then make sure
+ // that we emit 'end' on the very next tick.
+ if (state.ended && !state.endEmitted && state.length === 0)
+ endReadable(this);
+
+ return ret;
+};
+
+function chunkInvalid(state, chunk) {
+ var er = null;
+ if (!Buffer.isBuffer(chunk) &&
+ 'string' !== typeof chunk &&
+ chunk !== null &&
+ chunk !== undefined &&
+ !state.objectMode) {
+ er = new TypeError('Invalid non-string/buffer chunk');
+ }
+ return er;
+}
+
+
+function onEofChunk(stream, state) {
+ if (state.decoder && !state.ended) {
+ var chunk = state.decoder.end();
+ if (chunk && chunk.length) {
+ state.buffer.push(chunk);
+ state.length += state.objectMode ? 1 : chunk.length;
+ }
+ }
+ state.ended = true;
+
+ // if we've ended and we have some data left, then emit
+ // 'readable' now to make sure it gets picked up.
+ if (state.length > 0)
+ emitReadable(stream);
+ else
+ endReadable(stream);
+}
+
+// Don't emit readable right away in sync mode, because this can trigger
+// another read() call => stack overflow. This way, it might trigger
+// a nextTick recursion warning, but that's not so bad.
+function emitReadable(stream) {
+ var state = stream._readableState;
+ state.needReadable = false;
+ if (state.emittedReadable)
+ return;
+
+ state.emittedReadable = true;
+ if (state.sync)
+ process.nextTick(function() {
+ emitReadable_(stream);
+ });
+ else
+ emitReadable_(stream);
+}
+
+function emitReadable_(stream) {
+ stream.emit('readable');
+}
+
+
+// at this point, the user has presumably seen the 'readable' event,
+// and called read() to consume some data. that may have triggered
+// in turn another _read(n) call, in which case reading = true if
+// it's in progress.
+// However, if we're not ended, or reading, and the length < hwm,
+// then go ahead and try to read some more preemptively.
+function maybeReadMore(stream, state) {
+ if (!state.readingMore) {
+ state.readingMore = true;
+ process.nextTick(function() {
+ maybeReadMore_(stream, state);
+ });
+ }
+}
+
+function maybeReadMore_(stream, state) {
+ var len = state.length;
+ while (!state.reading && !state.flowing && !state.ended &&
+ state.length < state.highWaterMark) {
+ stream.read(0);
+ if (len === state.length)
+ // didn't get any data, stop spinning.
+ break;
+ else
+ len = state.length;
+ }
+ state.readingMore = false;
+}
+
+// abstract method. to be overridden in specific implementation classes.
+// call cb(er, data) where data is <= n in length.
+// for virtual (non-string, non-buffer) streams, "length" is somewhat
+// arbitrary, and perhaps not very meaningful.
+Readable.prototype._read = function(n) {
+ this.emit('error', new Error('not implemented'));
+};
+
+Readable.prototype.pipe = function(dest, pipeOpts) {
+ var src = this;
+ var state = this._readableState;
+
+ switch (state.pipesCount) {
+ case 0:
+ state.pipes = dest;
+ break;
+ case 1:
+ state.pipes = [state.pipes, dest];
+ break;
+ default:
+ state.pipes.push(dest);
+ break;
+ }
+ state.pipesCount += 1;
+
+ var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
+ dest !== process.stdout &&
+ dest !== process.stderr;
+
+ var endFn = doEnd ? onend : cleanup;
+ if (state.endEmitted)
+ process.nextTick(endFn);
+ else
+ src.once('end', endFn);
+
+ dest.on('unpipe', onunpipe);
+ function onunpipe(readable) {
+ if (readable !== src) return;
+ cleanup();
+ }
+
+ function onend() {
+ dest.end();
+ }
+
+ // when the dest drains, it reduces the awaitDrain counter
+ // on the source. This would be more elegant with a .once()
+ // handler in flow(), but adding and removing repeatedly is
+ // too slow.
+ var ondrain = pipeOnDrain(src);
+ dest.on('drain', ondrain);
+
+ function cleanup() {
+ // cleanup event handlers once the pipe is broken
+ dest.removeListener('close', onclose);
+ dest.removeListener('finish', onfinish);
+ dest.removeListener('drain', ondrain);
+ dest.removeListener('error', onerror);
+ dest.removeListener('unpipe', onunpipe);
+ src.removeListener('end', onend);
+ src.removeListener('end', cleanup);
+
+ // if the reader is waiting for a drain event from this
+ // specific writer, then it would cause it to never start
+ // flowing again.
+ // So, if this is awaiting a drain, then we just call it now.
+ // If we don't know, then assume that we are waiting for one.
+ if (!dest._writableState || dest._writableState.needDrain)
+ ondrain();
+ }
+
+ // if the dest has an error, then stop piping into it.
+ // however, don't suppress the throwing behavior for this.
+ function onerror(er) {
+ unpipe();
+ dest.removeListener('error', onerror);
+ if (EE.listenerCount(dest, 'error') === 0)
+ dest.emit('error', er);
+ }
+ // This is a brutally ugly hack to make sure that our error handler
+ // is attached before any userland ones. NEVER DO THIS.
+ if (!dest._events || !dest._events.error)
+ dest.on('error', onerror);
+ else if (isArray(dest._events.error))
+ dest._events.error.unshift(onerror);
+ else
+ dest._events.error = [onerror, dest._events.error];
+
+
+
+ // Both close and finish should trigger unpipe, but only once.
+ function onclose() {
+ dest.removeListener('finish', onfinish);
+ unpipe();
+ }
+ dest.once('close', onclose);
+ function onfinish() {
+ dest.removeListener('close', onclose);
+ unpipe();
+ }
+ dest.once('finish', onfinish);
+
+ function unpipe() {
+ src.unpipe(dest);
+ }
+
+ // tell the dest that it's being piped to
+ dest.emit('pipe', src);
+
+ // start the flow if it hasn't been started already.
+ if (!state.flowing) {
+ // the handler that waits for readable events after all
+ // the data gets sucked out in flow.
+ // This would be easier to follow with a .once() handler
+ // in flow(), but that is too slow.
+ this.on('readable', pipeOnReadable);
+
+ state.flowing = true;
+ process.nextTick(function() {
+ flow(src);
+ });
+ }
+
+ return dest;
+};
+
+function pipeOnDrain(src) {
+ return function() {
+ var dest = this;
+ var state = src._readableState;
+ state.awaitDrain--;
+ if (state.awaitDrain === 0)
+ flow(src);
+ };
+}
+
+function flow(src) {
+ var state = src._readableState;
+ var chunk;
+ state.awaitDrain = 0;
+
+ function write(dest, i, list) {
+ var written = dest.write(chunk);
+ if (false === written) {
+ state.awaitDrain++;
+ }
+ }
+
+ while (state.pipesCount && null !== (chunk = src.read())) {
+
+ if (state.pipesCount === 1)
+ write(state.pipes, 0, null);
+ else
+ forEach(state.pipes, write);
+
+ src.emit('data', chunk);
+
+ // if anyone needs a drain, then we have to wait for that.
+ if (state.awaitDrain > 0)
+ return;
+ }
+
+ // if every destination was unpiped, either before entering this
+ // function, or in the while loop, then stop flowing.
+ //
+ // NB: This is a pretty rare edge case.
+ if (state.pipesCount === 0) {
+ state.flowing = false;
+
+ // if there were data event listeners added, then switch to old mode.
+ if (EE.listenerCount(src, 'data') > 0)
+ emitDataEvents(src);
+ return;
+ }
+
+ // at this point, no one needed a drain, so we just ran out of data
+ // on the next readable event, start it over again.
+ state.ranOut = true;
+}
+
+function pipeOnReadable() {
+ if (this._readableState.ranOut) {
+ this._readableState.ranOut = false;
+ flow(this);
+ }
+}
+
+
+Readable.prototype.unpipe = function(dest) {
+ var state = this._readableState;
+
+ // if we're not piping anywhere, then do nothing.
+ if (state.pipesCount === 0)
+ return this;
+
+ // just one destination. most common case.
+ if (state.pipesCount === 1) {
+ // passed in one, but it's not the right one.
+ if (dest && dest !== state.pipes)
+ return this;
+
+ if (!dest)
+ dest = state.pipes;
+
+ // got a match.
+ state.pipes = null;
+ state.pipesCount = 0;
+ this.removeListener('readable', pipeOnReadable);
+ state.flowing = false;
+ if (dest)
+ dest.emit('unpipe', this);
+ return this;
+ }
+
+ // slow case. multiple pipe destinations.
+
+ if (!dest) {
+ // remove all.
+ var dests = state.pipes;
+ var len = state.pipesCount;
+ state.pipes = null;
+ state.pipesCount = 0;
+ this.removeListener('readable', pipeOnReadable);
+ state.flowing = false;
+
+ for (var i = 0; i < len; i++)
+ dests[i].emit('unpipe', this);
+ return this;
+ }
+
+ // try to find the right one.
+ var i = indexOf(state.pipes, dest);
+ if (i === -1)
+ return this;
+
+ state.pipes.splice(i, 1);
+ state.pipesCount -= 1;
+ if (state.pipesCount === 1)
+ state.pipes = state.pipes[0];
+
+ dest.emit('unpipe', this);
+
+ return this;
+};
+
+// set up data events if they are asked for
+// Ensure readable listeners eventually get something
+Readable.prototype.on = function(ev, fn) {
+ var res = Stream.prototype.on.call(this, ev, fn);
+
+ if (ev === 'data' && !this._readableState.flowing)
+ emitDataEvents(this);
+
+ if (ev === 'readable' && this.readable) {
+ var state = this._readableState;
+ if (!state.readableListening) {
+ state.readableListening = true;
+ state.emittedReadable = false;
+ state.needReadable = true;
+ if (!state.reading) {
+ this.read(0);
+ } else if (state.length) {
+ emitReadable(this, state);
+ }
+ }
+ }
+
+ return res;
+};
+Readable.prototype.addListener = Readable.prototype.on;
+
+// pause() and resume() are remnants of the legacy readable stream API
+// If the user uses them, then switch into old mode.
+Readable.prototype.resume = function() {
+ emitDataEvents(this);
+ this.read(0);
+ this.emit('resume');
+};
+
+Readable.prototype.pause = function() {
+ emitDataEvents(this, true);
+ this.emit('pause');
+};
+
+function emitDataEvents(stream, startPaused) {
+ var state = stream._readableState;
+
+ if (state.flowing) {
+ // https://github.com/isaacs/readable-stream/issues/16
+ throw new Error('Cannot switch to old mode now.');
+ }
+
+ var paused = startPaused || false;
+ var readable = false;
+
+ // convert to an old-style stream.
+ stream.readable = true;
+ stream.pipe = Stream.prototype.pipe;
+ stream.on = stream.addListener = Stream.prototype.on;
+
+ stream.on('readable', function() {
+ readable = true;
+
+ var c;
+ while (!paused && (null !== (c = stream.read())))
+ stream.emit('data', c);
+
+ if (c === null) {
+ readable = false;
+ stream._readableState.needReadable = true;
+ }
+ });
+
+ stream.pause = function() {
+ paused = true;
+ this.emit('pause');
+ };
+
+ stream.resume = function() {
+ paused = false;
+ if (readable)
+ process.nextTick(function() {
+ stream.emit('readable');
+ });
+ else
+ this.read(0);
+ this.emit('resume');
+ };
+
+ // now make it start, just in case it hadn't already.
+ stream.emit('readable');
+}
+
+// wrap an old-style stream as the async data source.
+// This is *not* part of the readable stream interface.
+// It is an ugly unfortunate mess of history.
+Readable.prototype.wrap = function(stream) {
+ var state = this._readableState;
+ var paused = false;
+
+ var self = this;
+ stream.on('end', function() {
+ if (state.decoder && !state.ended) {
+ var chunk = state.decoder.end();
+ if (chunk && chunk.length)
+ self.push(chunk);
+ }
+
+ self.push(null);
+ });
+
+ stream.on('data', function(chunk) {
+ if (state.decoder)
+ chunk = state.decoder.write(chunk);
+
+ // don't skip over falsy values in objectMode
+ //if (state.objectMode && util.isNullOrUndefined(chunk))
+ if (state.objectMode && (chunk === null || chunk === undefined))
+ return;
+ else if (!state.objectMode && (!chunk || !chunk.length))
+ return;
+
+ var ret = self.push(chunk);
+ if (!ret) {
+ paused = true;
+ stream.pause();
+ }
+ });
+
+ // proxy all the other methods.
+ // important when wrapping filters and duplexes.
+ for (var i in stream) {
+ if (typeof stream[i] === 'function' &&
+ typeof this[i] === 'undefined') {
+ this[i] = function(method) { return function() {
+ return stream[method].apply(stream, arguments);
+ }}(i);
+ }
+ }
+
+ // proxy certain important events.
+ var events = ['error', 'close', 'destroy', 'pause', 'resume'];
+ forEach(events, function(ev) {
+ stream.on(ev, self.emit.bind(self, ev));
+ });
+
+ // when we try to consume some more bytes, simply unpause the
+ // underlying stream.
+ self._read = function(n) {
+ if (paused) {
+ paused = false;
+ stream.resume();
+ }
+ };
+
+ return self;
+};
+
+
+
+// exposed for testing purposes only.
+Readable._fromList = fromList;
+
+// Pluck off n bytes from an array of buffers.
+// Length is the combined lengths of all the buffers in the list.
+function fromList(n, state) {
+ var list = state.buffer;
+ var length = state.length;
+ var stringMode = !!state.decoder;
+ var objectMode = !!state.objectMode;
+ var ret;
+
+ // nothing in the list, definitely empty.
+ if (list.length === 0)
+ return null;
+
+ if (length === 0)
+ ret = null;
+ else if (objectMode)
+ ret = list.shift();
+ else if (!n || n >= length) {
+ // read it all, truncate the array.
+ if (stringMode)
+ ret = list.join('');
+ else
+ ret = Buffer.concat(list, length);
+ list.length = 0;
+ } else {
+ // read just some of it.
+ if (n < list[0].length) {
+ // just take a part of the first list item.
+ // slice is the same for buffers and strings.
+ var buf = list[0];
+ ret = buf.slice(0, n);
+ list[0] = buf.slice(n);
+ } else if (n === list[0].length) {
+ // first list is a perfect match
+ ret = list.shift();
+ } else {
+ // complex case.
+ // we have enough to cover it, but it spans past the first buffer.
+ if (stringMode)
+ ret = '';
+ else
+ ret = new Buffer(n);
+
+ var c = 0;
+ for (var i = 0, l = list.length; i < l && c < n; i++) {
+ var buf = list[0];
+ var cpy = Math.min(n - c, buf.length);
+
+ if (stringMode)
+ ret += buf.slice(0, cpy);
+ else
+ buf.copy(ret, c, 0, cpy);
+
+ if (cpy < buf.length)
+ list[0] = buf.slice(cpy);
+ else
+ list.shift();
+
+ c += cpy;
+ }
+ }
+ }
+
+ return ret;
+}
+
+function endReadable(stream) {
+ var state = stream._readableState;
+
+ // If we get here before consuming all the bytes, then that is a
+ // bug in node. Should never happen.
+ if (state.length > 0)
+ throw new Error('endReadable called on non-empty stream');
+
+ if (!state.endEmitted && state.calledRead) {
+ state.ended = true;
+ process.nextTick(function() {
+ // Check that we didn't get one last unshift.
+ if (!state.endEmitted && state.length === 0) {
+ state.endEmitted = true;
+ stream.readable = false;
+ stream.emit('end');
+ }
+ });
+ }
+}
+
+function forEach (xs, f) {
+ for (var i = 0, l = xs.length; i < l; i++) {
+ f(xs[i], i);
+ }
+}
+
+function indexOf (xs, x) {
+ for (var i = 0, l = xs.length; i < l; i++) {
+ if (xs[i] === x) return i;
+ }
+ return -1;
+}
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+
+// a transform stream is a readable/writable stream where you do
+// something with the data. Sometimes it's called a "filter",
+// but that's not a great name for it, since that implies a thing where
+// some bits pass through, and others are simply ignored. (That would
+// be a valid example of a transform, of course.)
+//
+// While the output is causally related to the input, it's not a
+// necessarily symmetric or synchronous transformation. For example,
+// a zlib stream might take multiple plain-text writes(), and then
+// emit a single compressed chunk some time in the future.
+//
+// Here's how this works:
+//
+// The Transform stream has all the aspects of the readable and writable
+// stream classes. When you write(chunk), that calls _write(chunk,cb)
+// internally, and returns false if there's a lot of pending writes
+// buffered up. When you call read(), that calls _read(n) until
+// there's enough pending readable data buffered up.
+//
+// In a transform stream, the written data is placed in a buffer. When
+// _read(n) is called, it transforms the queued up data, calling the
+// buffered _write cb's as it consumes chunks. If consuming a single
+// written chunk would result in multiple output chunks, then the first
+// outputted bit calls the readcb, and subsequent chunks just go into
+// the read buffer, and will cause it to emit 'readable' if necessary.
+//
+// This way, back-pressure is actually determined by the reading side,
+// since _read has to be called to start processing a new chunk. However,
+// a pathological inflate type of transform can cause excessive buffering
+// here. For example, imagine a stream where every byte of input is
+// interpreted as an integer from 0-255, and then results in that many
+// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
+// 1kb of data being output. In this case, you could write a very small
+// amount of input, and end up with a very large amount of output. In
+// such a pathological inflating mechanism, there'd be no way to tell
+// the system to stop doing the transform. A single 4MB write could
+// cause the system to run out of memory.
+//
+// However, even in such a pathological case, only a single written chunk
+// would be consumed, and then the rest would wait (un-transformed) until
+// the results of the previous transformed chunk were consumed.
+
+module.exports = Transform;
+
+var Duplex = require('./_stream_duplex');
+
+/*<replacement>*/
+var util = require('core-util-is');
+util.inherits = require('inherits');
+/*</replacement>*/
+
+util.inherits(Transform, Duplex);
+
+
+function TransformState(options, stream) {
+ this.afterTransform = function(er, data) {
+ return afterTransform(stream, er, data);
+ };
+
+ this.needTransform = false;
+ this.transforming = false;
+ this.writecb = null;
+ this.writechunk = null;
+}
+
+function afterTransform(stream, er, data) {
+ var ts = stream._transformState;
+ ts.transforming = false;
+
+ var cb = ts.writecb;
+
+ if (!cb)
+ return stream.emit('error', new Error('no writecb in Transform class'));
+
+ ts.writechunk = null;
+ ts.writecb = null;
+
+ if (data !== null && data !== undefined)
+ stream.push(data);
+
+ if (cb)
+ cb(er);
+
+ var rs = stream._readableState;
+ rs.reading = false;
+ if (rs.needReadable || rs.length < rs.highWaterMark) {
+ stream._read(rs.highWaterMark);
+ }
+}
+
+
+function Transform(options) {
+ if (!(this instanceof Transform))
+ return new Transform(options);
+
+ Duplex.call(this, options);
+
+ var ts = this._transformState = new TransformState(options, this);
+
+ // when the writable side finishes, then flush out anything remaining.
+ var stream = this;
+
+ // start out asking for a readable event once data is transformed.
+ this._readableState.needReadable = true;
+
+ // we have implemented the _read method, and done the other things
+ // that Readable wants before the first _read call, so unset the
+ // sync guard flag.
+ this._readableState.sync = false;
+
+ this.once('finish', function() {
+ if ('function' === typeof this._flush)
+ this._flush(function(er) {
+ done(stream, er);
+ });
+ else
+ done(stream);
+ });
+}
+
+Transform.prototype.push = function(chunk, encoding) {
+ this._transformState.needTransform = false;
+ return Duplex.prototype.push.call(this, chunk, encoding);
+};
+
+// This is the part where you do stuff!
+// override this function in implementation classes.
+// 'chunk' is an input chunk.
+//
+// Call `push(newChunk)` to pass along transformed output
+// to the readable side. You may call 'push' zero or more times.
+//
+// Call `cb(err)` when you are done with this chunk. If you pass
+// an error, then that'll put the hurt on the whole operation. If you
+// never call cb(), then you'll never get another chunk.
+Transform.prototype._transform = function(chunk, encoding, cb) {
+ throw new Error('not implemented');
+};
+
+Transform.prototype._write = function(chunk, encoding, cb) {
+ var ts = this._transformState;
+ ts.writecb = cb;
+ ts.writechunk = chunk;
+ ts.writeencoding = encoding;
+ if (!ts.transforming) {
+ var rs = this._readableState;
+ if (ts.needTransform ||
+ rs.needReadable ||
+ rs.length < rs.highWaterMark)
+ this._read(rs.highWaterMark);
+ }
+};
+
+// Doesn't matter what the args are here.
+// _transform does all the work.
+// That we got here means that the readable side wants more data.
+Transform.prototype._read = function(n) {
+ var ts = this._transformState;
+
+ if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
+ ts.transforming = true;
+ this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
+ } else {
+ // mark that we need a transform, so that any data that comes in
+ // will get processed, now that we've asked for it.
+ ts.needTransform = true;
+ }
+};
+
+
+function done(stream, er) {
+ if (er)
+ return stream.emit('error', er);
+
+ // if there's nothing in the write buffer, then that means
+ // that nothing more will ever be provided
+ var ws = stream._writableState;
+ var rs = stream._readableState;
+ var ts = stream._transformState;
+
+ if (ws.length)
+ throw new Error('calling transform done when ws.length != 0');
+
+ if (ts.transforming)
+ throw new Error('calling transform done when still transforming');
+
+ return stream.push(null);
+}
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+// A bit simpler than readable streams.
+// Implement an async ._write(chunk, cb), and it'll handle all
+// the drain event emission and buffering.
+
+module.exports = Writable;
+
+/*<replacement>*/
+var Buffer = require('buffer').Buffer;
+/*</replacement>*/
+
+Writable.WritableState = WritableState;
+
+
+/*<replacement>*/
+var util = require('core-util-is');
+util.inherits = require('inherits');
+/*</replacement>*/
+
+var Stream = require('stream');
+
+util.inherits(Writable, Stream);
+
+function WriteReq(chunk, encoding, cb) {
+ this.chunk = chunk;
+ this.encoding = encoding;
+ this.callback = cb;
+}
+
+function WritableState(options, stream) {
+ options = options || {};
+
+ // the point at which write() starts returning false
+ // Note: 0 is a valid value, means that we always return false if
+ // the entire buffer is not flushed immediately on write()
+ var hwm = options.highWaterMark;
+ this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
+
+ // object stream flag to indicate whether or not this stream
+ // contains buffers or objects.
+ this.objectMode = !!options.objectMode;
+
+ // cast to ints.
+ this.highWaterMark = ~~this.highWaterMark;
+
+ this.needDrain = false;
+ // at the start of calling end()
+ this.ending = false;
+ // when end() has been called, and returned
+ this.ended = false;
+ // when 'finish' is emitted
+ this.finished = false;
+
+ // should we decode strings into buffers before passing to _write?
+ // this is here so that some node-core streams can optimize string
+ // handling at a lower level.
+ var noDecode = options.decodeStrings === false;
+ this.decodeStrings = !noDecode;
+
+ // Crypto is kind of old and crusty. Historically, its default string
+ // encoding is 'binary' so we have to make this configurable.
+ // Everything else in the universe uses 'utf8', though.
+ this.defaultEncoding = options.defaultEncoding || 'utf8';
+
+ // not an actual buffer we keep track of, but a measurement
+ // of how much we're waiting to get pushed to some underlying
+ // socket or file.
+ this.length = 0;
+
+ // a flag to see when we're in the middle of a write.
+ this.writing = false;
+
+ // a flag to be able to tell if the onwrite cb is called immediately,
+ // or on a later tick. We set this to true at first, becuase any
+ // actions that shouldn't happen until "later" should generally also
+ // not happen before the first write call.
+ this.sync = true;
+
+ // a flag to know if we're processing previously buffered items, which
+ // may call the _write() callback in the same tick, so that we don't
+ // end up in an overlapped onwrite situation.
+ this.bufferProcessing = false;
+
+ // the callback that's passed to _write(chunk,cb)
+ this.onwrite = function(er) {
+ onwrite(stream, er);
+ };
+
+ // the callback that the user supplies to write(chunk,encoding,cb)
+ this.writecb = null;
+
+ // the amount that is being written when _write is called.
+ this.writelen = 0;
+
+ this.buffer = [];
+
+ // True if the error was already emitted and should not be thrown again
+ this.errorEmitted = false;
+}
+
+function Writable(options) {
+ var Duplex = require('./_stream_duplex');
+
+ // Writable ctor is applied to Duplexes, though they're not
+ // instanceof Writable, they're instanceof Readable.
+ if (!(this instanceof Writable) && !(this instanceof Duplex))
+ return new Writable(options);
+
+ this._writableState = new WritableState(options, this);
+
+ // legacy.
+ this.writable = true;
+
+ Stream.call(this);
+}
+
+// Otherwise people can pipe Writable streams, which is just wrong.
+Writable.prototype.pipe = function() {
+ this.emit('error', new Error('Cannot pipe. Not readable.'));
+};
+
+
+function writeAfterEnd(stream, state, cb) {
+ var er = new Error('write after end');
+ // TODO: defer error events consistently everywhere, not just the cb
+ stream.emit('error', er);
+ process.nextTick(function() {
+ cb(er);
+ });
+}
+
+// If we get something that is not a buffer, string, null, or undefined,
+// and we're not in objectMode, then that's an error.
+// Otherwise stream chunks are all considered to be of length=1, and the
+// watermarks determine how many objects to keep in the buffer, rather than
+// how many bytes or characters.
+function validChunk(stream, state, chunk, cb) {
+ var valid = true;
+ if (!Buffer.isBuffer(chunk) &&
+ 'string' !== typeof chunk &&
+ chunk !== null &&
+ chunk !== undefined &&
+ !state.objectMode) {
+ var er = new TypeError('Invalid non-string/buffer chunk');
+ stream.emit('error', er);
+ process.nextTick(function() {
+ cb(er);
+ });
+ valid = false;
+ }
+ return valid;
+}
+
+Writable.prototype.write = function(chunk, encoding, cb) {
+ var state = this._writableState;
+ var ret = false;
+
+ if (typeof encoding === 'function') {
+ cb = encoding;
+ encoding = null;
+ }
+
+ if (Buffer.isBuffer(chunk))
+ encoding = 'buffer';
+ else if (!encoding)
+ encoding = state.defaultEncoding;
+
+ if (typeof cb !== 'function')
+ cb = function() {};
+
+ if (state.ended)
+ writeAfterEnd(this, state, cb);
+ else if (validChunk(this, state, chunk, cb))
+ ret = writeOrBuffer(this, state, chunk, encoding, cb);
+
+ return ret;
+};
+
+function decodeChunk(state, chunk, encoding) {
+ if (!state.objectMode &&
+ state.decodeStrings !== false &&
+ typeof chunk === 'string') {
+ chunk = new Buffer(chunk, encoding);
+ }
+ return chunk;
+}
+
+// if we're already writing something, then just put this
+// in the queue, and wait our turn. Otherwise, call _write
+// If we return false, then we need a drain event, so set that flag.
+function writeOrBuffer(stream, state, chunk, encoding, cb) {
+ chunk = decodeChunk(state, chunk, encoding);
+ if (Buffer.isBuffer(chunk))
+ encoding = 'buffer';
+ var len = state.objectMode ? 1 : chunk.length;
+
+ state.length += len;
+
+ var ret = state.length < state.highWaterMark;
+ // we must ensure that previous needDrain will not be reset to false.
+ if (!ret)
+ state.needDrain = true;
+
+ if (state.writing)
+ state.buffer.push(new WriteReq(chunk, encoding, cb));
+ else
+ doWrite(stream, state, len, chunk, encoding, cb);
+
+ return ret;
+}
+
+function doWrite(stream, state, len, chunk, encoding, cb) {
+ state.writelen = len;
+ state.writecb = cb;
+ state.writing = true;
+ state.sync = true;
+ stream._write(chunk, encoding, state.onwrite);
+ state.sync = false;
+}
+
+function onwriteError(stream, state, sync, er, cb) {
+ if (sync)
+ process.nextTick(function() {
+ cb(er);
+ });
+ else
+ cb(er);
+
+ stream._writableState.errorEmitted = true;
+ stream.emit('error', er);
+}
+
+function onwriteStateUpdate(state) {
+ state.writing = false;
+ state.writecb = null;
+ state.length -= state.writelen;
+ state.writelen = 0;
+}
+
+function onwrite(stream, er) {
+ var state = stream._writableState;
+ var sync = state.sync;
+ var cb = state.writecb;
+
+ onwriteStateUpdate(state);
+
+ if (er)
+ onwriteError(stream, state, sync, er, cb);
+ else {
+ // Check if we're actually ready to finish, but don't emit yet
+ var finished = needFinish(stream, state);
+
+ if (!finished && !state.bufferProcessing && state.buffer.length)
+ clearBuffer(stream, state);
+
+ if (sync) {
+ process.nextTick(function() {
+ afterWrite(stream, state, finished, cb);
+ });
+ } else {
+ afterWrite(stream, state, finished, cb);
+ }
+ }
+}
+
+function afterWrite(stream, state, finished, cb) {
+ if (!finished)
+ onwriteDrain(stream, state);
+ cb();
+ if (finished)
+ finishMaybe(stream, state);
+}
+
+// Must force callback to be called on nextTick, so that we don't
+// emit 'drain' before the write() consumer gets the 'false' return
+// value, and has a chance to attach a 'drain' listener.
+function onwriteDrain(stream, state) {
+ if (state.length === 0 && state.needDrain) {
+ state.needDrain = false;
+ stream.emit('drain');
+ }
+}
+
+
+// if there's something in the buffer waiting, then process it
+function clearBuffer(stream, state) {
+ state.bufferProcessing = true;
+
+ for (var c = 0; c < state.buffer.length; c++) {
+ var entry = state.buffer[c];
+ var chunk = entry.chunk;
+ var encoding = entry.encoding;
+ var cb = entry.callback;
+ var len = state.objectMode ? 1 : chunk.length;
+
+ doWrite(stream, state, len, chunk, encoding, cb);
+
+ // if we didn't call the onwrite immediately, then
+ // it means that we need to wait until it does.
+ // also, that means that the chunk and cb are currently
+ // being processed, so move the buffer counter past them.
+ if (state.writing) {
+ c++;
+ break;
+ }
+ }
+
+ state.bufferProcessing = false;
+ if (c < state.buffer.length)
+ state.buffer = state.buffer.slice(c);
+ else
+ state.buffer.length = 0;
+}
+
+Writable.prototype._write = function(chunk, encoding, cb) {
+ cb(new Error('not implemented'));
+};
+
+Writable.prototype.end = function(chunk, encoding, cb) {
+ var state = this._writableState;
+
+ if (typeof chunk === 'function') {
+ cb = chunk;
+ chunk = null;
+ encoding = null;
+ } else if (typeof encoding === 'function') {
+ cb = encoding;
+ encoding = null;
+ }
+
+ if (typeof chunk !== 'undefined' && chunk !== null)
+ this.write(chunk, encoding);
+
+ // ignore unnecessary end() calls.
+ if (!state.ending && !state.finished)
+ endWritable(this, state, cb);
+};
+
+
+function needFinish(stream, state) {
+ return (state.ending &&
+ state.length === 0 &&
+ !state.finished &&
+ !state.writing);
+}
+
+function finishMaybe(stream, state) {
+ var need = needFinish(stream, state);
+ if (need) {
+ state.finished = true;
+ stream.emit('finish');
+ }
+ return need;
+}
+
+function endWritable(stream, state, cb) {
+ state.ending = true;
+ finishMaybe(stream, state);
+ if (cb) {
+ if (state.finished)
+ process.nextTick(cb);
+ else
+ stream.once('finish', cb);
+ }
+ state.ended = true;
+}
--- /dev/null
+# core-util-is
+
+The `util.is*` functions introduced in Node v0.12.
--- /dev/null
+diff --git a/lib/util.js b/lib/util.js
+index a03e874..9074e8e 100644
+--- a/lib/util.js
++++ b/lib/util.js
+@@ -19,430 +19,6 @@
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-var formatRegExp = /%[sdj%]/g;
+-exports.format = function(f) {
+- if (!isString(f)) {
+- var objects = [];
+- for (var i = 0; i < arguments.length; i++) {
+- objects.push(inspect(arguments[i]));
+- }
+- return objects.join(' ');
+- }
+-
+- var i = 1;
+- var args = arguments;
+- var len = args.length;
+- var str = String(f).replace(formatRegExp, function(x) {
+- if (x === '%%') return '%';
+- if (i >= len) return x;
+- switch (x) {
+- case '%s': return String(args[i++]);
+- case '%d': return Number(args[i++]);
+- case '%j':
+- try {
+- return JSON.stringify(args[i++]);
+- } catch (_) {
+- return '[Circular]';
+- }
+- default:
+- return x;
+- }
+- });
+- for (var x = args[i]; i < len; x = args[++i]) {
+- if (isNull(x) || !isObject(x)) {
+- str += ' ' + x;
+- } else {
+- str += ' ' + inspect(x);
+- }
+- }
+- return str;
+-};
+-
+-
+-// Mark that a method should not be used.
+-// Returns a modified function which warns once by default.
+-// If --no-deprecation is set, then it is a no-op.
+-exports.deprecate = function(fn, msg) {
+- // Allow for deprecating things in the process of starting up.
+- if (isUndefined(global.process)) {
+- return function() {
+- return exports.deprecate(fn, msg).apply(this, arguments);
+- };
+- }
+-
+- if (process.noDeprecation === true) {
+- return fn;
+- }
+-
+- var warned = false;
+- function deprecated() {
+- if (!warned) {
+- if (process.throwDeprecation) {
+- throw new Error(msg);
+- } else if (process.traceDeprecation) {
+- console.trace(msg);
+- } else {
+- console.error(msg);
+- }
+- warned = true;
+- }
+- return fn.apply(this, arguments);
+- }
+-
+- return deprecated;
+-};
+-
+-
+-var debugs = {};
+-var debugEnviron;
+-exports.debuglog = function(set) {
+- if (isUndefined(debugEnviron))
+- debugEnviron = process.env.NODE_DEBUG || '';
+- set = set.toUpperCase();
+- if (!debugs[set]) {
+- if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
+- var pid = process.pid;
+- debugs[set] = function() {
+- var msg = exports.format.apply(exports, arguments);
+- console.error('%s %d: %s', set, pid, msg);
+- };
+- } else {
+- debugs[set] = function() {};
+- }
+- }
+- return debugs[set];
+-};
+-
+-
+-/**
+- * Echos the value of a value. Trys to print the value out
+- * in the best way possible given the different types.
+- *
+- * @param {Object} obj The object to print out.
+- * @param {Object} opts Optional options object that alters the output.
+- */
+-/* legacy: obj, showHidden, depth, colors*/
+-function inspect(obj, opts) {
+- // default options
+- var ctx = {
+- seen: [],
+- stylize: stylizeNoColor
+- };
+- // legacy...
+- if (arguments.length >= 3) ctx.depth = arguments[2];
+- if (arguments.length >= 4) ctx.colors = arguments[3];
+- if (isBoolean(opts)) {
+- // legacy...
+- ctx.showHidden = opts;
+- } else if (opts) {
+- // got an "options" object
+- exports._extend(ctx, opts);
+- }
+- // set default options
+- if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
+- if (isUndefined(ctx.depth)) ctx.depth = 2;
+- if (isUndefined(ctx.colors)) ctx.colors = false;
+- if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
+- if (ctx.colors) ctx.stylize = stylizeWithColor;
+- return formatValue(ctx, obj, ctx.depth);
+-}
+-exports.inspect = inspect;
+-
+-
+-// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+-inspect.colors = {
+- 'bold' : [1, 22],
+- 'italic' : [3, 23],
+- 'underline' : [4, 24],
+- 'inverse' : [7, 27],
+- 'white' : [37, 39],
+- 'grey' : [90, 39],
+- 'black' : [30, 39],
+- 'blue' : [34, 39],
+- 'cyan' : [36, 39],
+- 'green' : [32, 39],
+- 'magenta' : [35, 39],
+- 'red' : [31, 39],
+- 'yellow' : [33, 39]
+-};
+-
+-// Don't use 'blue' not visible on cmd.exe
+-inspect.styles = {
+- 'special': 'cyan',
+- 'number': 'yellow',
+- 'boolean': 'yellow',
+- 'undefined': 'grey',
+- 'null': 'bold',
+- 'string': 'green',
+- 'date': 'magenta',
+- // "name": intentionally not styling
+- 'regexp': 'red'
+-};
+-
+-
+-function stylizeWithColor(str, styleType) {
+- var style = inspect.styles[styleType];
+-
+- if (style) {
+- return '\u001b[' + inspect.colors[style][0] + 'm' + str +
+- '\u001b[' + inspect.colors[style][1] + 'm';
+- } else {
+- return str;
+- }
+-}
+-
+-
+-function stylizeNoColor(str, styleType) {
+- return str;
+-}
+-
+-
+-function arrayToHash(array) {
+- var hash = {};
+-
+- array.forEach(function(val, idx) {
+- hash[val] = true;
+- });
+-
+- return hash;
+-}
+-
+-
+-function formatValue(ctx, value, recurseTimes) {
+- // Provide a hook for user-specified inspect functions.
+- // Check that value is an object with an inspect function on it
+- if (ctx.customInspect &&
+- value &&
+- isFunction(value.inspect) &&
+- // Filter out the util module, it's inspect function is special
+- value.inspect !== exports.inspect &&
+- // Also filter out any prototype objects using the circular check.
+- !(value.constructor && value.constructor.prototype === value)) {
+- var ret = value.inspect(recurseTimes, ctx);
+- if (!isString(ret)) {
+- ret = formatValue(ctx, ret, recurseTimes);
+- }
+- return ret;
+- }
+-
+- // Primitive types cannot have properties
+- var primitive = formatPrimitive(ctx, value);
+- if (primitive) {
+- return primitive;
+- }
+-
+- // Look up the keys of the object.
+- var keys = Object.keys(value);
+- var visibleKeys = arrayToHash(keys);
+-
+- if (ctx.showHidden) {
+- keys = Object.getOwnPropertyNames(value);
+- }
+-
+- // Some type of object without properties can be shortcutted.
+- if (keys.length === 0) {
+- if (isFunction(value)) {
+- var name = value.name ? ': ' + value.name : '';
+- return ctx.stylize('[Function' + name + ']', 'special');
+- }
+- if (isRegExp(value)) {
+- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+- }
+- if (isDate(value)) {
+- return ctx.stylize(Date.prototype.toString.call(value), 'date');
+- }
+- if (isError(value)) {
+- return formatError(value);
+- }
+- }
+-
+- var base = '', array = false, braces = ['{', '}'];
+-
+- // Make Array say that they are Array
+- if (isArray(value)) {
+- array = true;
+- braces = ['[', ']'];
+- }
+-
+- // Make functions say that they are functions
+- if (isFunction(value)) {
+- var n = value.name ? ': ' + value.name : '';
+- base = ' [Function' + n + ']';
+- }
+-
+- // Make RegExps say that they are RegExps
+- if (isRegExp(value)) {
+- base = ' ' + RegExp.prototype.toString.call(value);
+- }
+-
+- // Make dates with properties first say the date
+- if (isDate(value)) {
+- base = ' ' + Date.prototype.toUTCString.call(value);
+- }
+-
+- // Make error with message first say the error
+- if (isError(value)) {
+- base = ' ' + formatError(value);
+- }
+-
+- if (keys.length === 0 && (!array || value.length == 0)) {
+- return braces[0] + base + braces[1];
+- }
+-
+- if (recurseTimes < 0) {
+- if (isRegExp(value)) {
+- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+- } else {
+- return ctx.stylize('[Object]', 'special');
+- }
+- }
+-
+- ctx.seen.push(value);
+-
+- var output;
+- if (array) {
+- output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
+- } else {
+- output = keys.map(function(key) {
+- return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
+- });
+- }
+-
+- ctx.seen.pop();
+-
+- return reduceToSingleString(output, base, braces);
+-}
+-
+-
+-function formatPrimitive(ctx, value) {
+- if (isUndefined(value))
+- return ctx.stylize('undefined', 'undefined');
+- if (isString(value)) {
+- var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
+- .replace(/'/g, "\\'")
+- .replace(/\\"/g, '"') + '\'';
+- return ctx.stylize(simple, 'string');
+- }
+- if (isNumber(value)) {
+- // Format -0 as '-0'. Strict equality won't distinguish 0 from -0,
+- // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 .
+- if (value === 0 && 1 / value < 0)
+- return ctx.stylize('-0', 'number');
+- return ctx.stylize('' + value, 'number');
+- }
+- if (isBoolean(value))
+- return ctx.stylize('' + value, 'boolean');
+- // For some reason typeof null is "object", so special case here.
+- if (isNull(value))
+- return ctx.stylize('null', 'null');
+-}
+-
+-
+-function formatError(value) {
+- return '[' + Error.prototype.toString.call(value) + ']';
+-}
+-
+-
+-function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
+- var output = [];
+- for (var i = 0, l = value.length; i < l; ++i) {
+- if (hasOwnProperty(value, String(i))) {
+- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+- String(i), true));
+- } else {
+- output.push('');
+- }
+- }
+- keys.forEach(function(key) {
+- if (!key.match(/^\d+$/)) {
+- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+- key, true));
+- }
+- });
+- return output;
+-}
+-
+-
+-function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
+- var name, str, desc;
+- desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
+- if (desc.get) {
+- if (desc.set) {
+- str = ctx.stylize('[Getter/Setter]', 'special');
+- } else {
+- str = ctx.stylize('[Getter]', 'special');
+- }
+- } else {
+- if (desc.set) {
+- str = ctx.stylize('[Setter]', 'special');
+- }
+- }
+- if (!hasOwnProperty(visibleKeys, key)) {
+- name = '[' + key + ']';
+- }
+- if (!str) {
+- if (ctx.seen.indexOf(desc.value) < 0) {
+- if (isNull(recurseTimes)) {
+- str = formatValue(ctx, desc.value, null);
+- } else {
+- str = formatValue(ctx, desc.value, recurseTimes - 1);
+- }
+- if (str.indexOf('\n') > -1) {
+- if (array) {
+- str = str.split('\n').map(function(line) {
+- return ' ' + line;
+- }).join('\n').substr(2);
+- } else {
+- str = '\n' + str.split('\n').map(function(line) {
+- return ' ' + line;
+- }).join('\n');
+- }
+- }
+- } else {
+- str = ctx.stylize('[Circular]', 'special');
+- }
+- }
+- if (isUndefined(name)) {
+- if (array && key.match(/^\d+$/)) {
+- return str;
+- }
+- name = JSON.stringify('' + key);
+- if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
+- name = name.substr(1, name.length - 2);
+- name = ctx.stylize(name, 'name');
+- } else {
+- name = name.replace(/'/g, "\\'")
+- .replace(/\\"/g, '"')
+- .replace(/(^"|"$)/g, "'");
+- name = ctx.stylize(name, 'string');
+- }
+- }
+-
+- return name + ': ' + str;
+-}
+-
+-
+-function reduceToSingleString(output, base, braces) {
+- var numLinesEst = 0;
+- var length = output.reduce(function(prev, cur) {
+- numLinesEst++;
+- if (cur.indexOf('\n') >= 0) numLinesEst++;
+- return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
+- }, 0);
+-
+- if (length > 60) {
+- return braces[0] +
+- (base === '' ? '' : base + '\n ') +
+- ' ' +
+- output.join(',\n ') +
+- ' ' +
+- braces[1];
+- }
+-
+- return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+-}
+-
+-
+ // NOTE: These type checking functions intentionally don't use `instanceof`
+ // because it is fragile and can be easily faked with `Object.create()`.
+ function isArray(ar) {
+@@ -522,166 +98,10 @@ function isPrimitive(arg) {
+ exports.isPrimitive = isPrimitive;
+
+ function isBuffer(arg) {
+- return arg instanceof Buffer;
++ return Buffer.isBuffer(arg);
+ }
+ exports.isBuffer = isBuffer;
+
+ function objectToString(o) {
+ return Object.prototype.toString.call(o);
+-}
+-
+-
+-function pad(n) {
+- return n < 10 ? '0' + n.toString(10) : n.toString(10);
+-}
+-
+-
+-var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+- 'Oct', 'Nov', 'Dec'];
+-
+-// 26 Feb 16:19:34
+-function timestamp() {
+- var d = new Date();
+- var time = [pad(d.getHours()),
+- pad(d.getMinutes()),
+- pad(d.getSeconds())].join(':');
+- return [d.getDate(), months[d.getMonth()], time].join(' ');
+-}
+-
+-
+-// log is just a thin wrapper to console.log that prepends a timestamp
+-exports.log = function() {
+- console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
+-};
+-
+-
+-/**
+- * Inherit the prototype methods from one constructor into another.
+- *
+- * The Function.prototype.inherits from lang.js rewritten as a standalone
+- * function (not on Function.prototype). NOTE: If this file is to be loaded
+- * during bootstrapping this function needs to be rewritten using some native
+- * functions as prototype setup using normal JavaScript does not work as
+- * expected during bootstrapping (see mirror.js in r114903).
+- *
+- * @param {function} ctor Constructor function which needs to inherit the
+- * prototype.
+- * @param {function} superCtor Constructor function to inherit prototype from.
+- */
+-exports.inherits = function(ctor, superCtor) {
+- ctor.super_ = superCtor;
+- ctor.prototype = Object.create(superCtor.prototype, {
+- constructor: {
+- value: ctor,
+- enumerable: false,
+- writable: true,
+- configurable: true
+- }
+- });
+-};
+-
+-exports._extend = function(origin, add) {
+- // Don't do anything if add isn't an object
+- if (!add || !isObject(add)) return origin;
+-
+- var keys = Object.keys(add);
+- var i = keys.length;
+- while (i--) {
+- origin[keys[i]] = add[keys[i]];
+- }
+- return origin;
+-};
+-
+-function hasOwnProperty(obj, prop) {
+- return Object.prototype.hasOwnProperty.call(obj, prop);
+-}
+-
+-
+-// Deprecated old stuff.
+-
+-exports.p = exports.deprecate(function() {
+- for (var i = 0, len = arguments.length; i < len; ++i) {
+- console.error(exports.inspect(arguments[i]));
+- }
+-}, 'util.p: Use console.error() instead');
+-
+-
+-exports.exec = exports.deprecate(function() {
+- return require('child_process').exec.apply(this, arguments);
+-}, 'util.exec is now called `child_process.exec`.');
+-
+-
+-exports.print = exports.deprecate(function() {
+- for (var i = 0, len = arguments.length; i < len; ++i) {
+- process.stdout.write(String(arguments[i]));
+- }
+-}, 'util.print: Use console.log instead');
+-
+-
+-exports.puts = exports.deprecate(function() {
+- for (var i = 0, len = arguments.length; i < len; ++i) {
+- process.stdout.write(arguments[i] + '\n');
+- }
+-}, 'util.puts: Use console.log instead');
+-
+-
+-exports.debug = exports.deprecate(function(x) {
+- process.stderr.write('DEBUG: ' + x + '\n');
+-}, 'util.debug: Use console.error instead');
+-
+-
+-exports.error = exports.deprecate(function(x) {
+- for (var i = 0, len = arguments.length; i < len; ++i) {
+- process.stderr.write(arguments[i] + '\n');
+- }
+-}, 'util.error: Use console.error instead');
+-
+-
+-exports.pump = exports.deprecate(function(readStream, writeStream, callback) {
+- var callbackCalled = false;
+-
+- function call(a, b, c) {
+- if (callback && !callbackCalled) {
+- callback(a, b, c);
+- callbackCalled = true;
+- }
+- }
+-
+- readStream.addListener('data', function(chunk) {
+- if (writeStream.write(chunk) === false) readStream.pause();
+- });
+-
+- writeStream.addListener('drain', function() {
+- readStream.resume();
+- });
+-
+- readStream.addListener('end', function() {
+- writeStream.end();
+- });
+-
+- readStream.addListener('close', function() {
+- call();
+- });
+-
+- readStream.addListener('error', function(err) {
+- writeStream.end();
+- call(err);
+- });
+-
+- writeStream.addListener('error', function(err) {
+- readStream.destroy();
+- call(err);
+- });
+-}, 'util.pump(): Use readableStream.pipe() instead');
+-
+-
+-var uv;
+-exports._errnoException = function(err, syscall) {
+- if (isUndefined(uv)) uv = process.binding('uv');
+- var errname = uv.errname(err);
+- var e = new Error(syscall + ' ' + errname);
+- e.code = errname;
+- e.errno = errname;
+- e.syscall = syscall;
+- return e;
+-};
++}
\ No newline at end of file
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+// NOTE: These type checking functions intentionally don't use `instanceof`
+// because it is fragile and can be easily faked with `Object.create()`.
+function isArray(ar) {
+ return Array.isArray(ar);
+}
+exports.isArray = isArray;
+
+function isBoolean(arg) {
+ return typeof arg === 'boolean';
+}
+exports.isBoolean = isBoolean;
+
+function isNull(arg) {
+ return arg === null;
+}
+exports.isNull = isNull;
+
+function isNullOrUndefined(arg) {
+ return arg == null;
+}
+exports.isNullOrUndefined = isNullOrUndefined;
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+exports.isNumber = isNumber;
+
+function isString(arg) {
+ return typeof arg === 'string';
+}
+exports.isString = isString;
+
+function isSymbol(arg) {
+ return typeof arg === 'symbol';
+}
+exports.isSymbol = isSymbol;
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+exports.isUndefined = isUndefined;
+
+function isRegExp(re) {
+ return isObject(re) && objectToString(re) === '[object RegExp]';
+}
+exports.isRegExp = isRegExp;
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+exports.isObject = isObject;
+
+function isDate(d) {
+ return isObject(d) && objectToString(d) === '[object Date]';
+}
+exports.isDate = isDate;
+
+function isError(e) {
+ return isObject(e) &&
+ (objectToString(e) === '[object Error]' || e instanceof Error);
+}
+exports.isError = isError;
+
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+exports.isFunction = isFunction;
+
+function isPrimitive(arg) {
+ return arg === null ||
+ typeof arg === 'boolean' ||
+ typeof arg === 'number' ||
+ typeof arg === 'string' ||
+ typeof arg === 'symbol' || // ES6 symbol
+ typeof arg === 'undefined';
+}
+exports.isPrimitive = isPrimitive;
+
+function isBuffer(arg) {
+ return Buffer.isBuffer(arg);
+}
+exports.isBuffer = isBuffer;
+
+function objectToString(o) {
+ return Object.prototype.toString.call(o);
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "core-util-is",
+ "version": "1.0.1",
+ "description": "The `util.is*` functions introduced in Node v0.12.",
+ "main": "lib/util.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/core-util-is.git"
+ },
+ "keywords": [
+ "util",
+ "isBuffer",
+ "isArray",
+ "isNumber",
+ "isString",
+ "isRegExp",
+ "isThis",
+ "isThat",
+ "polyfill"
+ ],
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/isaacs/core-util-is/issues"
+ },
+ "readme": "# core-util-is\n\nThe `util.is*` functions introduced in Node v0.12.\n",
+ "readmeFilename": "README.md",
+ "homepage": "https://github.com/isaacs/core-util-is",
+ "_id": "core-util-is@1.0.1",
+ "dist": {
+ "shasum": "6b07085aef9a3ccac6ee53bf9d3df0c1521a5538",
+ "tarball": "http://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "_from": "core-util-is@>=1.0.0 <1.1.0",
+ "_npmVersion": "1.3.23",
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ "maintainers": [
+ {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ }
+ ],
+ "directories": {},
+ "_shasum": "6b07085aef9a3ccac6ee53bf9d3df0c1521a5538",
+ "_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz",
+ "scripts": {}
+}
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+// NOTE: These type checking functions intentionally don't use `instanceof`
+// because it is fragile and can be easily faked with `Object.create()`.
+function isArray(ar) {
+ return Array.isArray(ar);
+}
+exports.isArray = isArray;
+
+function isBoolean(arg) {
+ return typeof arg === 'boolean';
+}
+exports.isBoolean = isBoolean;
+
+function isNull(arg) {
+ return arg === null;
+}
+exports.isNull = isNull;
+
+function isNullOrUndefined(arg) {
+ return arg == null;
+}
+exports.isNullOrUndefined = isNullOrUndefined;
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+exports.isNumber = isNumber;
+
+function isString(arg) {
+ return typeof arg === 'string';
+}
+exports.isString = isString;
+
+function isSymbol(arg) {
+ return typeof arg === 'symbol';
+}
+exports.isSymbol = isSymbol;
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+exports.isUndefined = isUndefined;
+
+function isRegExp(re) {
+ return isObject(re) && objectToString(re) === '[object RegExp]';
+}
+exports.isRegExp = isRegExp;
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+exports.isObject = isObject;
+
+function isDate(d) {
+ return isObject(d) && objectToString(d) === '[object Date]';
+}
+exports.isDate = isDate;
+
+function isError(e) {
+ return isObject(e) && objectToString(e) === '[object Error]';
+}
+exports.isError = isError;
+
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+exports.isFunction = isFunction;
+
+function isPrimitive(arg) {
+ return arg === null ||
+ typeof arg === 'boolean' ||
+ typeof arg === 'number' ||
+ typeof arg === 'string' ||
+ typeof arg === 'symbol' || // ES6 symbol
+ typeof arg === 'undefined';
+}
+exports.isPrimitive = isPrimitive;
+
+function isBuffer(arg) {
+ return arg instanceof Buffer;
+}
+exports.isBuffer = isBuffer;
+
+function objectToString(o) {
+ return Object.prototype.toString.call(o);
+}
--- /dev/null
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
--- /dev/null
+Browser-friendly inheritance fully compatible with standard node.js
+[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).
+
+This package exports standard `inherits` from node.js `util` module in
+node environment, but also provides alternative browser-friendly
+implementation through [browser
+field](https://gist.github.com/shtylman/4339901). Alternative
+implementation is a literal copy of standard one located in standalone
+module to avoid requiring of `util`. It also has a shim for old
+browsers with no `Object.create` support.
+
+While keeping you sure you are using standard `inherits`
+implementation in node.js environment, it allows bundlers such as
+[browserify](https://github.com/substack/node-browserify) to not
+include full `util` package to your client code if all you need is
+just `inherits` function. It worth, because browser shim for `util`
+package is large and `inherits` is often the single function you need
+from it.
+
+It's recommended to use this package instead of
+`require('util').inherits` for any code that has chances to be used
+not only in node.js but in browser too.
+
+## usage
+
+```js
+var inherits = require('inherits');
+// then use exactly as the standard one
+```
+
+## note on version ~1.0
+
+Version ~1.0 had completely different motivation and is not compatible
+neither with 2.0 nor with standard node.js `inherits`.
+
+If you are using version ~1.0 and planning to switch to ~2.0, be
+careful:
+
+* new version uses `super_` instead of `super` for referencing
+ superclass
+* new version overwrites current prototype while old one preserves any
+ existing fields on it
--- /dev/null
+module.exports = require('util').inherits
--- /dev/null
+if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ };
+} else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+}
--- /dev/null
+{
+ "name": "inherits",
+ "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()",
+ "version": "2.0.1",
+ "keywords": [
+ "inheritance",
+ "class",
+ "klass",
+ "oop",
+ "object-oriented",
+ "inherits",
+ "browser",
+ "browserify"
+ ],
+ "main": "./inherits.js",
+ "browser": "./inherits_browser.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/inherits.git"
+ },
+ "license": "ISC",
+ "scripts": {
+ "test": "node test"
+ },
+ "bugs": {
+ "url": "https://github.com/isaacs/inherits/issues"
+ },
+ "_id": "inherits@2.0.1",
+ "dist": {
+ "shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
+ "tarball": "http://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "_from": "inherits@>=2.0.1 <2.1.0",
+ "_npmVersion": "1.3.8",
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ "maintainers": [
+ {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ }
+ ],
+ "directories": {},
+ "_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
+ "_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "readme": "ERROR: No README data found!",
+ "homepage": "https://github.com/isaacs/inherits"
+}
--- /dev/null
+var inherits = require('./inherits.js')
+var assert = require('assert')
+
+function test(c) {
+ assert(c.constructor === Child)
+ assert(c.constructor.super_ === Parent)
+ assert(Object.getPrototypeOf(c) === Child.prototype)
+ assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype)
+ assert(c instanceof Child)
+ assert(c instanceof Parent)
+}
+
+function Child() {
+ Parent.call(this)
+ test(this)
+}
+
+function Parent() {}
+
+inherits(Child, Parent)
+
+var c = new Child
+test(c)
+
+console.log('ok')
--- /dev/null
+
+# isarray
+
+`Array#isArray` for older browsers.
+
+## Usage
+
+```js
+var isArray = require('isarray');
+
+console.log(isArray([])); // => true
+console.log(isArray({})); // => false
+```
+
+## Installation
+
+With [npm](http://npmjs.org) do
+
+```bash
+$ npm install isarray
+```
+
+Then bundle for the browser with
+[browserify](https://github.com/substack/browserify).
+
+With [component](http://component.io) do
+
+```bash
+$ component install juliangruber/isarray
+```
+
+## License
+
+(MIT)
+
+Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
+
+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.
--- /dev/null
+
+/**
+ * Require the given path.
+ *
+ * @param {String} path
+ * @return {Object} exports
+ * @api public
+ */
+
+function require(path, parent, orig) {
+ var resolved = require.resolve(path);
+
+ // lookup failed
+ if (null == resolved) {
+ orig = orig || path;
+ parent = parent || 'root';
+ var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
+ err.path = orig;
+ err.parent = parent;
+ err.require = true;
+ throw err;
+ }
+
+ var module = require.modules[resolved];
+
+ // perform real require()
+ // by invoking the module's
+ // registered function
+ if (!module.exports) {
+ module.exports = {};
+ module.client = module.component = true;
+ module.call(this, module.exports, require.relative(resolved), module);
+ }
+
+ return module.exports;
+}
+
+/**
+ * Registered modules.
+ */
+
+require.modules = {};
+
+/**
+ * Registered aliases.
+ */
+
+require.aliases = {};
+
+/**
+ * Resolve `path`.
+ *
+ * Lookup:
+ *
+ * - PATH/index.js
+ * - PATH.js
+ * - PATH
+ *
+ * @param {String} path
+ * @return {String} path or null
+ * @api private
+ */
+
+require.resolve = function(path) {
+ if (path.charAt(0) === '/') path = path.slice(1);
+ var index = path + '/index.js';
+
+ var paths = [
+ path,
+ path + '.js',
+ path + '.json',
+ path + '/index.js',
+ path + '/index.json'
+ ];
+
+ for (var i = 0; i < paths.length; i++) {
+ var path = paths[i];
+ if (require.modules.hasOwnProperty(path)) return path;
+ }
+
+ if (require.aliases.hasOwnProperty(index)) {
+ return require.aliases[index];
+ }
+};
+
+/**
+ * Normalize `path` relative to the current path.
+ *
+ * @param {String} curr
+ * @param {String} path
+ * @return {String}
+ * @api private
+ */
+
+require.normalize = function(curr, path) {
+ var segs = [];
+
+ if ('.' != path.charAt(0)) return path;
+
+ curr = curr.split('/');
+ path = path.split('/');
+
+ for (var i = 0; i < path.length; ++i) {
+ if ('..' == path[i]) {
+ curr.pop();
+ } else if ('.' != path[i] && '' != path[i]) {
+ segs.push(path[i]);
+ }
+ }
+
+ return curr.concat(segs).join('/');
+};
+
+/**
+ * Register module at `path` with callback `definition`.
+ *
+ * @param {String} path
+ * @param {Function} definition
+ * @api private
+ */
+
+require.register = function(path, definition) {
+ require.modules[path] = definition;
+};
+
+/**
+ * Alias a module definition.
+ *
+ * @param {String} from
+ * @param {String} to
+ * @api private
+ */
+
+require.alias = function(from, to) {
+ if (!require.modules.hasOwnProperty(from)) {
+ throw new Error('Failed to alias "' + from + '", it does not exist');
+ }
+ require.aliases[to] = from;
+};
+
+/**
+ * Return a require function relative to the `parent` path.
+ *
+ * @param {String} parent
+ * @return {Function}
+ * @api private
+ */
+
+require.relative = function(parent) {
+ var p = require.normalize(parent, '..');
+
+ /**
+ * lastIndexOf helper.
+ */
+
+ function lastIndexOf(arr, obj) {
+ var i = arr.length;
+ while (i--) {
+ if (arr[i] === obj) return i;
+ }
+ return -1;
+ }
+
+ /**
+ * The relative require() itself.
+ */
+
+ function localRequire(path) {
+ var resolved = localRequire.resolve(path);
+ return require(resolved, parent, path);
+ }
+
+ /**
+ * Resolve relative to the parent.
+ */
+
+ localRequire.resolve = function(path) {
+ var c = path.charAt(0);
+ if ('/' == c) return path.slice(1);
+ if ('.' == c) return require.normalize(p, path);
+
+ // resolve deps by returning
+ // the dep in the nearest "deps"
+ // directory
+ var segs = parent.split('/');
+ var i = lastIndexOf(segs, 'deps') + 1;
+ if (!i) i = 0;
+ path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
+ return path;
+ };
+
+ /**
+ * Check if module is defined at `path`.
+ */
+
+ localRequire.exists = function(path) {
+ return require.modules.hasOwnProperty(localRequire.resolve(path));
+ };
+
+ return localRequire;
+};
+require.register("isarray/index.js", function(exports, require, module){
+module.exports = Array.isArray || function (arr) {
+ return Object.prototype.toString.call(arr) == '[object Array]';
+};
+
+});
+require.alias("isarray/index.js", "isarray/index.js");
+
--- /dev/null
+{
+ "name" : "isarray",
+ "description" : "Array#isArray for older browsers",
+ "version" : "0.0.1",
+ "repository" : "juliangruber/isarray",
+ "homepage": "https://github.com/juliangruber/isarray",
+ "main" : "index.js",
+ "scripts" : [
+ "index.js"
+ ],
+ "dependencies" : {},
+ "keywords": ["browser","isarray","array"],
+ "author": {
+ "name": "Julian Gruber",
+ "email": "mail@juliangruber.com",
+ "url": "http://juliangruber.com"
+ },
+ "license": "MIT"
+}
--- /dev/null
+module.exports = Array.isArray || function (arr) {
+ return Object.prototype.toString.call(arr) == '[object Array]';
+};
--- /dev/null
+{
+ "name": "isarray",
+ "description": "Array#isArray for older browsers",
+ "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/juliangruber/isarray.git"
+ },
+ "homepage": "https://github.com/juliangruber/isarray",
+ "main": "index.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "*"
+ },
+ "keywords": [
+ "browser",
+ "isarray",
+ "array"
+ ],
+ "author": {
+ "name": "Julian Gruber",
+ "email": "mail@juliangruber.com",
+ "url": "http://juliangruber.com"
+ },
+ "license": "MIT",
+ "_id": "isarray@0.0.1",
+ "dist": {
+ "shasum": "8a18acfca9a8f4177e09abfc6038939b05d1eedf",
+ "tarball": "http://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "_from": "isarray@0.0.1",
+ "_npmVersion": "1.2.18",
+ "_npmUser": {
+ "name": "juliangruber",
+ "email": "julian@juliangruber.com"
+ },
+ "maintainers": [
+ {
+ "name": "juliangruber",
+ "email": "julian@juliangruber.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "8a18acfca9a8f4177e09abfc6038939b05d1eedf",
+ "_resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+}
--- /dev/null
+build
+test
--- /dev/null
+Copyright Joyent, Inc. and other Node 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.
--- /dev/null
+**string_decoder.js** (`require('string_decoder')`) from Node.js core
+
+Copyright Joyent, Inc. and other Node contributors. See LICENCE file for details.
+
+Version numbers match the versions found in Node core, e.g. 0.10.24 matches Node 0.10.24, likewise 0.11.10 matches Node 0.11.10. **Prefer the stable version over the unstable.**
+
+The *build/* directory contains a build script that will scrape the source from the [joyent/node](https://github.com/joyent/node) repo given a specific Node version.
\ No newline at end of file
--- /dev/null
+// Copyright Joyent, Inc. and other Node 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.
+
+var Buffer = require('buffer').Buffer;
+
+var isBufferEncoding = Buffer.isEncoding
+ || function(encoding) {
+ switch (encoding && encoding.toLowerCase()) {
+ case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true;
+ default: return false;
+ }
+ }
+
+
+function assertEncoding(encoding) {
+ if (encoding && !isBufferEncoding(encoding)) {
+ throw new Error('Unknown encoding: ' + encoding);
+ }
+}
+
+// StringDecoder provides an interface for efficiently splitting a series of
+// buffers into a series of JS strings without breaking apart multi-byte
+// characters. CESU-8 is handled as part of the UTF-8 encoding.
+//
+// @TODO Handling all encodings inside a single object makes it very difficult
+// to reason about this code, so it should be split up in the future.
+// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code
+// points as used by CESU-8.
+var StringDecoder = exports.StringDecoder = function(encoding) {
+ this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, '');
+ assertEncoding(encoding);
+ switch (this.encoding) {
+ case 'utf8':
+ // CESU-8 represents each of Surrogate Pair by 3-bytes
+ this.surrogateSize = 3;
+ break;
+ case 'ucs2':
+ case 'utf16le':
+ // UTF-16 represents each of Surrogate Pair by 2-bytes
+ this.surrogateSize = 2;
+ this.detectIncompleteChar = utf16DetectIncompleteChar;
+ break;
+ case 'base64':
+ // Base-64 stores 3 bytes in 4 chars, and pads the remainder.
+ this.surrogateSize = 3;
+ this.detectIncompleteChar = base64DetectIncompleteChar;
+ break;
+ default:
+ this.write = passThroughWrite;
+ return;
+ }
+
+ // Enough space to store all bytes of a single character. UTF-8 needs 4
+ // bytes, but CESU-8 may require up to 6 (3 bytes per surrogate).
+ this.charBuffer = new Buffer(6);
+ // Number of bytes received for the current incomplete multi-byte character.
+ this.charReceived = 0;
+ // Number of bytes expected for the current incomplete multi-byte character.
+ this.charLength = 0;
+};
+
+
+// write decodes the given buffer and returns it as JS string that is
+// guaranteed to not contain any partial multi-byte characters. Any partial
+// character found at the end of the buffer is buffered up, and will be
+// returned when calling write again with the remaining bytes.
+//
+// Note: Converting a Buffer containing an orphan surrogate to a String
+// currently works, but converting a String to a Buffer (via `new Buffer`, or
+// Buffer#write) will replace incomplete surrogates with the unicode
+// replacement character. See https://codereview.chromium.org/121173009/ .
+StringDecoder.prototype.write = function(buffer) {
+ var charStr = '';
+ // if our last write ended with an incomplete multibyte character
+ while (this.charLength) {
+ // determine how many remaining bytes this buffer has to offer for this char
+ var available = (buffer.length >= this.charLength - this.charReceived) ?
+ this.charLength - this.charReceived :
+ buffer.length;
+
+ // add the new bytes to the char buffer
+ buffer.copy(this.charBuffer, this.charReceived, 0, available);
+ this.charReceived += available;
+
+ if (this.charReceived < this.charLength) {
+ // still not enough chars in this buffer? wait for more ...
+ return '';
+ }
+
+ // remove bytes belonging to the current character from the buffer
+ buffer = buffer.slice(available, buffer.length);
+
+ // get the character that was split
+ charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding);
+
+ // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character
+ var charCode = charStr.charCodeAt(charStr.length - 1);
+ if (charCode >= 0xD800 && charCode <= 0xDBFF) {
+ this.charLength += this.surrogateSize;
+ charStr = '';
+ continue;
+ }
+ this.charReceived = this.charLength = 0;
+
+ // if there are no more bytes in this buffer, just emit our char
+ if (buffer.length === 0) {
+ return charStr;
+ }
+ break;
+ }
+
+ // determine and set charLength / charReceived
+ this.detectIncompleteChar(buffer);
+
+ var end = buffer.length;
+ if (this.charLength) {
+ // buffer the incomplete character bytes we got
+ buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end);
+ end -= this.charReceived;
+ }
+
+ charStr += buffer.toString(this.encoding, 0, end);
+
+ var end = charStr.length - 1;
+ var charCode = charStr.charCodeAt(end);
+ // CESU-8: lead surrogate (D800-DBFF) is also the incomplete character
+ if (charCode >= 0xD800 && charCode <= 0xDBFF) {
+ var size = this.surrogateSize;
+ this.charLength += size;
+ this.charReceived += size;
+ this.charBuffer.copy(this.charBuffer, size, 0, size);
+ buffer.copy(this.charBuffer, 0, 0, size);
+ return charStr.substring(0, end);
+ }
+
+ // or just emit the charStr
+ return charStr;
+};
+
+// detectIncompleteChar determines if there is an incomplete UTF-8 character at
+// the end of the given buffer. If so, it sets this.charLength to the byte
+// length that character, and sets this.charReceived to the number of bytes
+// that are available for this character.
+StringDecoder.prototype.detectIncompleteChar = function(buffer) {
+ // determine how many bytes we have to check at the end of this buffer
+ var i = (buffer.length >= 3) ? 3 : buffer.length;
+
+ // Figure out if one of the last i bytes of our buffer announces an
+ // incomplete char.
+ for (; i > 0; i--) {
+ var c = buffer[buffer.length - i];
+
+ // See http://en.wikipedia.org/wiki/UTF-8#Description
+
+ // 110XXXXX
+ if (i == 1 && c >> 5 == 0x06) {
+ this.charLength = 2;
+ break;
+ }
+
+ // 1110XXXX
+ if (i <= 2 && c >> 4 == 0x0E) {
+ this.charLength = 3;
+ break;
+ }
+
+ // 11110XXX
+ if (i <= 3 && c >> 3 == 0x1E) {
+ this.charLength = 4;
+ break;
+ }
+ }
+ this.charReceived = i;
+};
+
+StringDecoder.prototype.end = function(buffer) {
+ var res = '';
+ if (buffer && buffer.length)
+ res = this.write(buffer);
+
+ if (this.charReceived) {
+ var cr = this.charReceived;
+ var buf = this.charBuffer;
+ var enc = this.encoding;
+ res += buf.slice(0, cr).toString(enc);
+ }
+
+ return res;
+};
+
+function passThroughWrite(buffer) {
+ return buffer.toString(this.encoding);
+}
+
+function utf16DetectIncompleteChar(buffer) {
+ this.charReceived = buffer.length % 2;
+ this.charLength = this.charReceived ? 2 : 0;
+}
+
+function base64DetectIncompleteChar(buffer) {
+ this.charReceived = buffer.length % 3;
+ this.charLength = this.charReceived ? 3 : 0;
+}
--- /dev/null
+{
+ "name": "string_decoder",
+ "version": "0.10.31",
+ "description": "The string_decoder module from Node core",
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "~0.4.8"
+ },
+ "scripts": {
+ "test": "tap test/simple/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/rvagg/string_decoder.git"
+ },
+ "homepage": "https://github.com/rvagg/string_decoder",
+ "keywords": [
+ "string",
+ "decoder",
+ "browser",
+ "browserify"
+ ],
+ "license": "MIT",
+ "gitHead": "d46d4fd87cf1d06e031c23f1ba170ca7d4ade9a0",
+ "bugs": {
+ "url": "https://github.com/rvagg/string_decoder/issues"
+ },
+ "_id": "string_decoder@0.10.31",
+ "_shasum": "62e203bc41766c6c28c9fc84301dab1c5310fa94",
+ "_from": "string_decoder@>=0.10.0 <0.11.0",
+ "_npmVersion": "1.4.23",
+ "_npmUser": {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ },
+ "maintainers": [
+ {
+ "name": "substack",
+ "email": "mail@substack.net"
+ },
+ {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ }
+ ],
+ "dist": {
+ "shasum": "62e203bc41766c6c28c9fc84301dab1c5310fa94",
+ "tarball": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+{
+ "name": "readable-stream",
+ "version": "1.0.33",
+ "description": "Streams2, a user-land copy of the stream library from Node.js v0.10.x",
+ "main": "readable.js",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x",
+ "inherits": "~2.0.1"
+ },
+ "devDependencies": {
+ "tap": "~0.2.6"
+ },
+ "scripts": {
+ "test": "tap test/simple/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/readable-stream.git"
+ },
+ "keywords": [
+ "readable",
+ "stream",
+ "pipe"
+ ],
+ "browser": {
+ "util": false
+ },
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": "MIT",
+ "gitHead": "0bf97a117c5646556548966409ebc57a6dda2638",
+ "bugs": {
+ "url": "https://github.com/isaacs/readable-stream/issues"
+ },
+ "homepage": "https://github.com/isaacs/readable-stream",
+ "_id": "readable-stream@1.0.33",
+ "_shasum": "3a360dd66c1b1d7fd4705389860eda1d0f61126c",
+ "_from": "readable-stream@>=1.0.26 <1.1.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ },
+ "maintainers": [
+ {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ {
+ "name": "tootallnate",
+ "email": "nathan@tootallnate.net"
+ },
+ {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ }
+ ],
+ "dist": {
+ "shasum": "3a360dd66c1b1d7fd4705389860eda1d0f61126c",
+ "tarball": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+module.exports = require("./lib/_stream_passthrough.js")
--- /dev/null
+var Stream = require('stream'); // hack to fix a circular dependency issue when used with browserify
+exports = module.exports = require('./lib/_stream_readable.js');
+exports.Stream = Stream;
+exports.Readable = exports;
+exports.Writable = require('./lib/_stream_writable.js');
+exports.Duplex = require('./lib/_stream_duplex.js');
+exports.Transform = require('./lib/_stream_transform.js');
+exports.PassThrough = require('./lib/_stream_passthrough.js');
--- /dev/null
+module.exports = require("./lib/_stream_transform.js")
--- /dev/null
+module.exports = require("./lib/_stream_writable.js")
--- /dev/null
+{
+ "name": "bl",
+ "version": "0.9.4",
+ "description": "Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!",
+ "main": "bl.js",
+ "scripts": {
+ "test": "node test/test.js | faucet",
+ "test-local": "brtapsauce-local test/basic-test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rvagg/bl.git"
+ },
+ "homepage": "https://github.com/rvagg/bl",
+ "authors": [
+ "Rod Vagg <rod@vagg.org> (https://github.com/rvagg)",
+ "Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
+ "Jarett Cruger <jcrugzz@gmail.com> (https://github.com/jcrugzz)"
+ ],
+ "keywords": [
+ "buffer",
+ "buffers",
+ "stream",
+ "awesomesauce"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "~1.0.26"
+ },
+ "devDependencies": {
+ "tape": "~2.12.3",
+ "hash_file": "~0.1.1",
+ "faucet": "~0.0.1",
+ "brtapsauce": "~0.3.0"
+ },
+ "gitHead": "e7f90703c5f90ca26f60455ea6ad0b6be4a9feee",
+ "bugs": {
+ "url": "https://github.com/rvagg/bl/issues"
+ },
+ "_id": "bl@0.9.4",
+ "_shasum": "4702ddf72fbe0ecd82787c00c113aea1935ad0e7",
+ "_from": "bl@>=0.9.0 <0.10.0",
+ "_npmVersion": "2.1.18",
+ "_nodeVersion": "1.0.3",
+ "_npmUser": {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ },
+ "maintainers": [
+ {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ }
+ ],
+ "dist": {
+ "shasum": "4702ddf72fbe0ecd82787c00c113aea1935ad0e7",
+ "tarball": "http://registry.npmjs.org/bl/-/bl-0.9.4.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+var tape = require('tape')
+ , crypto = require('crypto')
+ , fs = require('fs')
+ , hash = require('hash_file')
+ , BufferList = require('../')
+
+ , encodings =
+ ('hex utf8 utf-8 ascii binary base64'
+ + (process.browser ? '' : ' ucs2 ucs-2 utf16le utf-16le')).split(' ')
+
+tape('single bytes from single buffer', function (t) {
+ var bl = new BufferList()
+ bl.append(new Buffer('abcd'))
+
+ t.equal(bl.length, 4)
+
+ t.equal(bl.get(0), 97)
+ t.equal(bl.get(1), 98)
+ t.equal(bl.get(2), 99)
+ t.equal(bl.get(3), 100)
+
+ t.end()
+})
+
+tape('single bytes from multiple buffers', function (t) {
+ var bl = new BufferList()
+ bl.append(new Buffer('abcd'))
+ bl.append(new Buffer('efg'))
+ bl.append(new Buffer('hi'))
+ bl.append(new Buffer('j'))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.get(0), 97)
+ t.equal(bl.get(1), 98)
+ t.equal(bl.get(2), 99)
+ t.equal(bl.get(3), 100)
+ t.equal(bl.get(4), 101)
+ t.equal(bl.get(5), 102)
+ t.equal(bl.get(6), 103)
+ t.equal(bl.get(7), 104)
+ t.equal(bl.get(8), 105)
+ t.equal(bl.get(9), 106)
+ t.end()
+})
+
+tape('multi bytes from single buffer', function (t) {
+ var bl = new BufferList()
+ bl.append(new Buffer('abcd'))
+
+ t.equal(bl.length, 4)
+
+ t.equal(bl.slice(0, 4).toString('ascii'), 'abcd')
+ t.equal(bl.slice(0, 3).toString('ascii'), 'abc')
+ t.equal(bl.slice(1, 4).toString('ascii'), 'bcd')
+
+ t.end()
+})
+
+tape('multiple bytes from multiple buffers', function (t) {
+ var bl = new BufferList()
+
+ bl.append(new Buffer('abcd'))
+ bl.append(new Buffer('efg'))
+ bl.append(new Buffer('hi'))
+ bl.append(new Buffer('j'))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+ t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
+ t.equal(bl.slice(3, 6).toString('ascii'), 'def')
+ t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
+ t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
+
+ t.end()
+})
+
+tape('multiple bytes from multiple buffer lists', function (t) {
+ var bl = new BufferList()
+
+ bl.append(new BufferList([new Buffer('abcd'), new Buffer('efg')]))
+ bl.append(new BufferList([new Buffer('hi'), new Buffer('j')]))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+ t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
+ t.equal(bl.slice(3, 6).toString('ascii'), 'def')
+ t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
+ t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
+
+ t.end()
+})
+
+tape('consuming from multiple buffers', function (t) {
+ var bl = new BufferList()
+
+ bl.append(new Buffer('abcd'))
+ bl.append(new Buffer('efg'))
+ bl.append(new Buffer('hi'))
+ bl.append(new Buffer('j'))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+
+ bl.consume(3)
+ t.equal(bl.length, 7)
+ t.equal(bl.slice(0, 7).toString('ascii'), 'defghij')
+
+ bl.consume(2)
+ t.equal(bl.length, 5)
+ t.equal(bl.slice(0, 5).toString('ascii'), 'fghij')
+
+ bl.consume(1)
+ t.equal(bl.length, 4)
+ t.equal(bl.slice(0, 4).toString('ascii'), 'ghij')
+
+ bl.consume(1)
+ t.equal(bl.length, 3)
+ t.equal(bl.slice(0, 3).toString('ascii'), 'hij')
+
+ bl.consume(2)
+ t.equal(bl.length, 1)
+ t.equal(bl.slice(0, 1).toString('ascii'), 'j')
+
+ t.end()
+})
+
+tape('test readUInt8 / readInt8', function (t) {
+ var buf1 = new Buffer(1)
+ , buf2 = new Buffer(3)
+ , buf3 = new Buffer(3)
+ , bl = new BufferList()
+
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUInt8(2), 0x3)
+ t.equal(bl.readInt8(2), 0x3)
+ t.equal(bl.readUInt8(3), 0x4)
+ t.equal(bl.readInt8(3), 0x4)
+ t.equal(bl.readUInt8(4), 0x23)
+ t.equal(bl.readInt8(4), 0x23)
+ t.equal(bl.readUInt8(5), 0x42)
+ t.equal(bl.readInt8(5), 0x42)
+ t.end()
+})
+
+tape('test readUInt16LE / readUInt16BE / readInt16LE / readInt16BE', function (t) {
+ var buf1 = new Buffer(1)
+ , buf2 = new Buffer(3)
+ , buf3 = new Buffer(3)
+ , bl = new BufferList()
+
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUInt16BE(2), 0x0304)
+ t.equal(bl.readUInt16LE(2), 0x0403)
+ t.equal(bl.readInt16BE(2), 0x0304)
+ t.equal(bl.readInt16LE(2), 0x0403)
+ t.equal(bl.readUInt16BE(3), 0x0423)
+ t.equal(bl.readUInt16LE(3), 0x2304)
+ t.equal(bl.readInt16BE(3), 0x0423)
+ t.equal(bl.readInt16LE(3), 0x2304)
+ t.equal(bl.readUInt16BE(4), 0x2342)
+ t.equal(bl.readUInt16LE(4), 0x4223)
+ t.equal(bl.readInt16BE(4), 0x2342)
+ t.equal(bl.readInt16LE(4), 0x4223)
+ t.end()
+})
+
+tape('test readUInt32LE / readUInt32BE / readInt32LE / readInt32BE', function (t) {
+ var buf1 = new Buffer(1)
+ , buf2 = new Buffer(3)
+ , buf3 = new Buffer(3)
+ , bl = new BufferList()
+
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUInt32BE(2), 0x03042342)
+ t.equal(bl.readUInt32LE(2), 0x42230403)
+ t.equal(bl.readInt32BE(2), 0x03042342)
+ t.equal(bl.readInt32LE(2), 0x42230403)
+ t.end()
+})
+
+tape('test readFloatLE / readFloatBE', function (t) {
+ var buf1 = new Buffer(1)
+ , buf2 = new Buffer(3)
+ , buf3 = new Buffer(3)
+ , bl = new BufferList()
+
+ buf2[1] = 0x00
+ buf2[2] = 0x00
+ buf3[0] = 0x80
+ buf3[1] = 0x3f
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readFloatLE(2), 0x01)
+ t.end()
+})
+
+tape('test readDoubleLE / readDoubleBE', function (t) {
+ var buf1 = new Buffer(1)
+ , buf2 = new Buffer(3)
+ , buf3 = new Buffer(10)
+ , bl = new BufferList()
+
+ buf2[1] = 0x55
+ buf2[2] = 0x55
+ buf3[0] = 0x55
+ buf3[1] = 0x55
+ buf3[2] = 0x55
+ buf3[3] = 0x55
+ buf3[4] = 0xd5
+ buf3[5] = 0x3f
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readDoubleLE(2), 0.3333333333333333)
+ t.end()
+})
+
+tape('test toString', function (t) {
+ var bl = new BufferList()
+
+ bl.append(new Buffer('abcd'))
+ bl.append(new Buffer('efg'))
+ bl.append(new Buffer('hi'))
+ bl.append(new Buffer('j'))
+
+ t.equal(bl.toString('ascii', 0, 10), 'abcdefghij')
+ t.equal(bl.toString('ascii', 3, 10), 'defghij')
+ t.equal(bl.toString('ascii', 3, 6), 'def')
+ t.equal(bl.toString('ascii', 3, 8), 'defgh')
+ t.equal(bl.toString('ascii', 5, 10), 'fghij')
+
+ t.end()
+})
+
+tape('test toString encoding', function (t) {
+ var bl = new BufferList()
+ , b = new Buffer('abcdefghij\xff\x00')
+
+ bl.append(new Buffer('abcd'))
+ bl.append(new Buffer('efg'))
+ bl.append(new Buffer('hi'))
+ bl.append(new Buffer('j'))
+ bl.append(new Buffer('\xff\x00'))
+
+ encodings.forEach(function (enc) {
+ t.equal(bl.toString(enc), b.toString(enc), enc)
+ })
+
+ t.end()
+})
+
+!process.browser && tape('test stream', function (t) {
+ var random = crypto.randomBytes(65534)
+ , rndhash = hash(random, 'md5')
+ , md5sum = crypto.createHash('md5')
+ , bl = new BufferList(function (err, buf) {
+ t.ok(Buffer.isBuffer(buf))
+ t.ok(err === null)
+ t.equal(rndhash, hash(bl.slice(), 'md5'))
+ t.equal(rndhash, hash(buf, 'md5'))
+
+ bl.pipe(fs.createWriteStream('/tmp/bl_test_rnd_out.dat'))
+ .on('close', function () {
+ var s = fs.createReadStream('/tmp/bl_test_rnd_out.dat')
+ s.on('data', md5sum.update.bind(md5sum))
+ s.on('end', function() {
+ t.equal(rndhash, md5sum.digest('hex'), 'woohoo! correct hash!')
+ t.end()
+ })
+ })
+
+ })
+
+ fs.writeFileSync('/tmp/bl_test_rnd.dat', random)
+ fs.createReadStream('/tmp/bl_test_rnd.dat').pipe(bl)
+})
+
+tape('instantiation with Buffer', function (t) {
+ var buf = crypto.randomBytes(1024)
+ , buf2 = crypto.randomBytes(1024)
+ , b = BufferList(buf)
+
+ t.equal(buf.toString('hex'), b.slice().toString('hex'), 'same buffer')
+ b = BufferList([ buf, buf2 ])
+ t.equal(b.slice().toString('hex'), Buffer.concat([ buf, buf2 ]).toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('test String appendage', function (t) {
+ var bl = new BufferList()
+ , b = new Buffer('abcdefghij\xff\x00')
+
+ bl.append('abcd')
+ bl.append('efg')
+ bl.append('hi')
+ bl.append('j')
+ bl.append('\xff\x00')
+
+ encodings.forEach(function (enc) {
+ t.equal(bl.toString(enc), b.toString(enc))
+ })
+
+ t.end()
+})
+
+tape('write nothing, should get empty buffer', function (t) {
+ t.plan(3)
+ BufferList(function (err, data) {
+ t.notOk(err, 'no error')
+ t.ok(Buffer.isBuffer(data), 'got a buffer')
+ t.equal(0, data.length, 'got a zero-length buffer')
+ t.end()
+ }).end()
+})
+
+tape('unicode string', function (t) {
+ t.plan(2)
+ var inp1 = '\u2600'
+ , inp2 = '\u2603'
+ , exp = inp1 + ' and ' + inp2
+ , bl = BufferList()
+ bl.write(inp1)
+ bl.write(' and ')
+ bl.write(inp2)
+ t.equal(exp, bl.toString())
+ t.equal(new Buffer(exp).toString('hex'), bl.toString('hex'))
+})
+
+tape('should emit finish', function (t) {
+ var source = BufferList()
+ , dest = BufferList()
+
+ source.write('hello')
+ source.pipe(dest)
+
+ dest.on('finish', function () {
+ t.equal(dest.toString('utf8'), 'hello')
+ t.end()
+ })
+})
+
+tape('basic copy', function (t) {
+ var buf = crypto.randomBytes(1024)
+ , buf2 = new Buffer(1024)
+ , b = BufferList(buf)
+
+ b.copy(buf2)
+ t.equal(b.slice().toString('hex'), buf2.toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('copy after many appends', function (t) {
+ var buf = crypto.randomBytes(512)
+ , buf2 = new Buffer(1024)
+ , b = BufferList(buf)
+
+ b.append(buf)
+ b.copy(buf2)
+ t.equal(b.slice().toString('hex'), buf2.toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('copy at a precise position', function (t) {
+ var buf = crypto.randomBytes(1004)
+ , buf2 = new Buffer(1024)
+ , b = BufferList(buf)
+
+ b.copy(buf2, 20)
+ t.equal(b.slice().toString('hex'), buf2.slice(20).toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('copy starting from a precise location', function (t) {
+ var buf = crypto.randomBytes(10)
+ , buf2 = new Buffer(5)
+ , b = BufferList(buf)
+
+ b.copy(buf2, 0, 5)
+ t.equal(b.slice(5).toString('hex'), buf2.toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('copy in an interval', function (t) {
+ var rnd = crypto.randomBytes(10)
+ , b = BufferList(rnd) // put the random bytes there
+ , actual = new Buffer(3)
+ , expected = new Buffer(3)
+
+ rnd.copy(expected, 0, 5, 8)
+ b.copy(actual, 0, 5, 8)
+
+ t.equal(actual.toString('hex'), expected.toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('copy an interval between two buffers', function (t) {
+ var buf = crypto.randomBytes(10)
+ , buf2 = new Buffer(10)
+ , b = BufferList(buf)
+
+ b.append(buf)
+ b.copy(buf2, 0, 5, 15)
+
+ t.equal(b.slice(5, 15).toString('hex'), buf2.toString('hex'), 'same buffer')
+ t.end()
+})
+
+tape('duplicate', function (t) {
+ t.plan(2)
+
+ var bl = new BufferList('abcdefghij\xff\x00')
+ , dup = bl.duplicate()
+
+ t.equal(bl.prototype, dup.prototype)
+ t.equal(bl.toString('hex'), dup.toString('hex'))
+})
+
+tape('destroy no pipe', function (t) {
+ t.plan(2)
+
+ var bl = new BufferList('alsdkfja;lsdkfja;lsdk')
+ bl.destroy()
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+})
+
+!process.browser && tape('destroy with pipe before read end', function (t) {
+ t.plan(2)
+
+ var bl = new BufferList()
+ fs.createReadStream(__dirname + '/sauce.js')
+ .pipe(bl)
+
+ bl.destroy()
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+
+})
+
+!process.browser && tape('destroy with pipe before read end with race', function (t) {
+ t.plan(2)
+
+ var bl = new BufferList()
+ fs.createReadStream(__dirname + '/sauce.js')
+ .pipe(bl)
+
+ setTimeout(function () {
+ bl.destroy()
+ setTimeout(function () {
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+ }, 500)
+ }, 500)
+})
+
+!process.browser && tape('destroy with pipe after read end', function (t) {
+ t.plan(2)
+
+ var bl = new BufferList()
+ fs.createReadStream(__dirname + '/sauce.js')
+ .on('end', onEnd)
+ .pipe(bl)
+
+ function onEnd () {
+ bl.destroy()
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+ }
+})
+
+!process.browser && tape('destroy with pipe while writing to a destination', function (t) {
+ t.plan(4)
+
+ var bl = new BufferList()
+ , ds = new BufferList()
+
+ fs.createReadStream(__dirname + '/sauce.js')
+ .on('end', onEnd)
+ .pipe(bl)
+
+ function onEnd () {
+ bl.pipe(ds)
+
+ setTimeout(function () {
+ bl.destroy()
+
+ t.equals(bl._bufs.length, 0)
+ t.equals(bl.length, 0)
+
+ ds.destroy()
+
+ t.equals(bl._bufs.length, 0)
+ t.equals(bl.length, 0)
+
+ }, 100)
+ }
+})
+
+!process.browser && tape('handle error', function (t) {
+ t.plan(2)
+ fs.createReadStream('/does/not/exist').pipe(BufferList(function (err, data) {
+ t.ok(err instanceof Error, 'has error')
+ t.notOk(data, 'no data')
+ }))
+})
--- /dev/null
+#!/usr/bin/env node
+
+const user = process.env.SAUCE_USER
+ , key = process.env.SAUCE_KEY
+ , path = require('path')
+ , brtapsauce = require('brtapsauce')
+ , testFile = path.join(__dirname, 'basic-test.js')
+
+ , capabilities = [
+ { browserName: 'chrome' , platform: 'Windows XP', version: '' }
+ , { browserName: 'firefox' , platform: 'Windows 8' , version: '' }
+ , { browserName: 'firefox' , platform: 'Windows XP', version: '4' }
+ , { browserName: 'internet explorer' , platform: 'Windows 8' , version: '10' }
+ , { browserName: 'internet explorer' , platform: 'Windows 7' , version: '9' }
+ , { browserName: 'internet explorer' , platform: 'Windows 7' , version: '8' }
+ , { browserName: 'internet explorer' , platform: 'Windows XP', version: '7' }
+ , { browserName: 'internet explorer' , platform: 'Windows XP', version: '6' }
+ , { browserName: 'safari' , platform: 'Windows 7' , version: '5' }
+ , { browserName: 'safari' , platform: 'OS X 10.8' , version: '6' }
+ , { browserName: 'opera' , platform: 'Windows 7' , version: '' }
+ , { browserName: 'opera' , platform: 'Windows 7' , version: '11' }
+ , { browserName: 'ipad' , platform: 'OS X 10.8' , version: '6' }
+ , { browserName: 'android' , platform: 'Linux' , version: '4.0', 'device-type': 'tablet' }
+ ]
+
+if (!user)
+ throw new Error('Must set a SAUCE_USER env var')
+if (!key)
+ throw new Error('Must set a SAUCE_KEY env var')
+
+brtapsauce({
+ name : 'Traversty'
+ , user : user
+ , key : key
+ , brsrc : testFile
+ , capabilities : capabilities
+ , options : { timeout: 60 * 6 }
+})
\ No newline at end of file
--- /dev/null
+require('./basic-test')
+
+if (!process.env.SAUCE_KEY || !process.env.SAUCE_USER)
+ return console.log('SAUCE_KEY and/or SAUCE_USER not set, not running sauce tests')
+
+if (!/v0\.10/.test(process.version))
+ return console.log('Not Node v0.10.x, not running sauce tests')
+
+require('./sauce.js')
\ No newline at end of file
--- /dev/null
+## Caseless -- wrap an object to set and get property with caseless semantics but also preserve caseing.
+
+This library is incredibly useful when working with HTTP headers. It allows you to get/set/check for headers in a caseless manner while also preserving the caseing of headers the first time they are set.
+
+## Usage
+
+```javascript
+var headers = {}
+ , c = caseless(headers)
+ ;
+c.set('a-Header', 'asdf')
+c.get('a-header') === 'asdf'
+```
+
+## has(key)
+
+Has takes a name and if it finds a matching header will return that header name with the preserved caseing it was set with.
+
+```javascript
+c.has('a-header') === 'a-Header'
+```
+
+## set(key, value[, clobber=true])
+
+Set is fairly straight forward except that if the header exists and clobber is disabled it will add `','+value` to the existing header.
+
+```javascript
+c.set('a-Header', 'fdas')
+c.set('a-HEADER', 'more', false)
+c.get('a-header') === 'fdsa,more'
+```
+
+## swap(key)
+
+Swaps the casing of a header with the new one that is passed in.
+
+```javascript
+var headers = {}
+ , c = caseless(headers)
+ ;
+c.set('a-Header', 'fdas')
+c.swap('a-HEADER')
+c.has('a-header') === 'a-HEADER'
+headers === {'a-HEADER': 'fdas'}
+```
--- /dev/null
+function Caseless (dict) {
+ this.dict = dict || {}
+}
+Caseless.prototype.set = function (name, value, clobber) {
+ if (typeof name === 'object') {
+ for (var i in name) {
+ this.set(i, name[i], value)
+ }
+ } else {
+ if (typeof clobber === 'undefined') clobber = true
+ var has = this.has(name)
+
+ if (!clobber && has) this.dict[has] = this.dict[has] + ',' + value
+ else this.dict[has || name] = value
+ return has
+ }
+}
+Caseless.prototype.has = function (name) {
+ var keys = Object.keys(this.dict)
+ , name = name.toLowerCase()
+ ;
+ for (var i=0;i<keys.length;i++) {
+ if (keys[i].toLowerCase() === name) return keys[i]
+ }
+ return false
+}
+Caseless.prototype.get = function (name) {
+ name = name.toLowerCase()
+ var result, _key
+ var headers = this.dict
+ Object.keys(headers).forEach(function (key) {
+ _key = key.toLowerCase()
+ if (name === _key) result = headers[key]
+ })
+ return result
+}
+Caseless.prototype.swap = function (name) {
+ var has = this.has(name)
+ if (!has) throw new Error('There is no header than matches "'+name+'"')
+ this.dict[name] = this.dict[has]
+ delete this.dict[has]
+}
+Caseless.prototype.del = function (name) {
+ var has = this.has(name)
+ return delete this.dict[has || name]
+}
+
+module.exports = function (dict) {return new Caseless(dict)}
+module.exports.httpify = function (resp, headers) {
+ var c = new Caseless(headers)
+ resp.setHeader = function (key, value, clobber) {
+ return c.set(key, value, clobber)
+ }
+ resp.hasHeader = function (key) {
+ return c.has(key)
+ }
+ resp.getHeader = function (key) {
+ return c.get(key)
+ }
+ resp.removeHeader = function (key) {
+ return c.del(key)
+ }
+ resp.headers = c.dict
+ return c
+}
--- /dev/null
+{
+ "name": "caseless",
+ "version": "0.9.0",
+ "description": "Caseless object set/get/has, very useful when working with HTTP headers.",
+ "main": "index.js",
+ "scripts": {
+ "test": "node test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/mikeal/caseless.git"
+ },
+ "keywords": [
+ "headers",
+ "http",
+ "caseless"
+ ],
+ "test": "node test.js",
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ "license": "BSD",
+ "bugs": {
+ "url": "https://github.com/mikeal/caseless/issues"
+ },
+ "devDependencies": {
+ "tape": "^2.10.2"
+ },
+ "gitHead": "5ff0ccebbbf14dad5dc91def1f274887801db3e3",
+ "homepage": "https://github.com/mikeal/caseless",
+ "_id": "caseless@0.9.0",
+ "_shasum": "b7b65ce6bf1413886539cfd533f0b30effa9cf88",
+ "_from": "caseless@>=0.9.0 <0.10.0",
+ "_npmVersion": "1.4.14",
+ "_npmUser": {
+ "name": "nylen",
+ "email": "jnylen@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ {
+ "name": "nylen",
+ "email": "jnylen@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "b7b65ce6bf1413886539cfd533f0b30effa9cf88",
+ "tarball": "http://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+var tape = require('tape')
+ , caseless = require('./')
+ ;
+
+tape('set get has', function (t) {
+ var headers = {}
+ , c = caseless(headers)
+ ;
+ t.plan(17)
+ c.set('a-Header', 'asdf')
+ t.equal(c.get('a-header'), 'asdf')
+ t.equal(c.has('a-header'), 'a-Header')
+ t.ok(!c.has('nothing'))
+ // old bug where we used the wrong regex
+ t.ok(!c.has('a-hea'))
+ c.set('a-header', 'fdsa')
+ t.equal(c.get('a-header'), 'fdsa')
+ t.equal(c.get('a-Header'), 'fdsa')
+ c.set('a-HEADER', 'more', false)
+ t.equal(c.get('a-header'), 'fdsa,more')
+
+ t.deepEqual(headers, {'a-Header': 'fdsa,more'})
+ c.swap('a-HEADER')
+ t.deepEqual(headers, {'a-HEADER': 'fdsa,more'})
+
+ c.set('deleteme', 'foobar')
+ t.ok(c.has('deleteme'))
+ t.ok(c.del('deleteme'))
+ t.notOk(c.has('deleteme'))
+ t.notOk(c.has('idonotexist'))
+ t.ok(c.del('idonotexist'))
+
+ c.set('tva', 'test1')
+ c.set('tva-header', 'test2')
+ t.equal(c.has('tva'), 'tva')
+ t.notOk(c.has('header'))
+
+ t.equal(c.get('tva'), 'test1')
+
+})
--- /dev/null
+Copyright (c) 2011 Debuggable Limited <felix@debuggable.com>
+
+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.
--- /dev/null
+# combined-stream [](https://travis-ci.org/felixge/node-combined-stream)
+
+A stream that emits multiple other streams one after another.
+
+## Installation
+
+``` bash
+npm install combined-stream
+```
+
+## Usage
+
+Here is a simple example that shows how you can use combined-stream to combine
+two files into one:
+
+``` javascript
+var CombinedStream = require('combined-stream');
+var fs = require('fs');
+
+var combinedStream = CombinedStream.create();
+combinedStream.append(fs.createReadStream('file1.txt'));
+combinedStream.append(fs.createReadStream('file2.txt'));
+
+combinedStream.pipe(fs.createWriteStream('combined.txt'));
+```
+
+While the example above works great, it will pause all source streams until
+they are needed. If you don't want that to happen, you can set `pauseStreams`
+to `false`:
+
+``` javascript
+var CombinedStream = require('combined-stream');
+var fs = require('fs');
+
+var combinedStream = CombinedStream.create({pauseStreams: false});
+combinedStream.append(fs.createReadStream('file1.txt'));
+combinedStream.append(fs.createReadStream('file2.txt'));
+
+combinedStream.pipe(fs.createWriteStream('combined.txt'));
+```
+
+However, what if you don't have all the source streams yet, or you don't want
+to allocate the resources (file descriptors, memory, etc.) for them right away?
+Well, in that case you can simply provide a callback that supplies the stream
+by calling a `next()` function:
+
+``` javascript
+var CombinedStream = require('combined-stream');
+var fs = require('fs');
+
+var combinedStream = CombinedStream.create();
+combinedStream.append(function(next) {
+ next(fs.createReadStream('file1.txt'));
+});
+combinedStream.append(function(next) {
+ next(fs.createReadStream('file2.txt'));
+});
+
+combinedStream.pipe(fs.createWriteStream('combined.txt'));
+```
+
+## API
+
+### CombinedStream.create([options])
+
+Returns a new combined stream object. Available options are:
+
+* `maxDataSize`
+* `pauseStreams`
+
+The effect of those options is described below.
+
+### combinedStream.pauseStreams = `true`
+
+Whether to apply back pressure to the underlaying streams. If set to `false`,
+the underlaying streams will never be paused. If set to `true`, the
+underlaying streams will be paused right after being appended, as well as when
+`delayedStream.pipe()` wants to throttle.
+
+### combinedStream.maxDataSize = `2 * 1024 * 1024`
+
+The maximum amount of bytes (or characters) to buffer for all source streams.
+If this value is exceeded, `combinedStream` emits an `'error'` event.
+
+### combinedStream.dataSize = `0`
+
+The amount of bytes (or characters) currently buffered by `combinedStream`.
+
+### combinedStream.append(stream)
+
+Appends the given `stream` to the combinedStream object. If `pauseStreams` is
+set to `true, this stream will also be paused right away.
+
+`streams` can also be a function that takes one parameter called `next`. `next`
+is a function that must be invoked in order to provide the `next` stream, see
+example above.
+
+Regardless of how the `stream` is appended, combined-stream always attaches an
+`'error'` listener to it, so you don't have to do that manually.
+
+Special case: `stream` can also be a String or Buffer.
+
+### combinedStream.write(data)
+
+You should not call this, `combinedStream` takes care of piping the appended
+streams into itself for you.
+
+### combinedStream.resume()
+
+Causes `combinedStream` to start drain the streams it manages. The function is
+idempotent, and also emits a `'resume'` event each time which usually goes to
+the stream that is currently being drained.
+
+### combinedStream.pause();
+
+If `combinedStream.pauseStreams` is set to `false`, this does nothing.
+Otherwise a `'pause'` event is emitted, this goes to the stream that is
+currently being drained, so you can use it to apply back pressure.
+
+### combinedStream.end();
+
+Sets `combinedStream.writable` to false, emits an `'end'` event, and removes
+all streams from the queue.
+
+### combinedStream.destroy();
+
+Same as `combinedStream.end()`, except it emits a `'close'` event instead of
+`'end'`.
+
+## License
+
+combined-stream is licensed under the MIT license.
--- /dev/null
+var util = require('util');
+var Stream = require('stream').Stream;
+var DelayedStream = require('delayed-stream');
+
+module.exports = CombinedStream;
+function CombinedStream() {
+ this.writable = false;
+ this.readable = true;
+ this.dataSize = 0;
+ this.maxDataSize = 2 * 1024 * 1024;
+ this.pauseStreams = true;
+
+ this._released = false;
+ this._streams = [];
+ this._currentStream = null;
+}
+util.inherits(CombinedStream, Stream);
+
+CombinedStream.create = function(options) {
+ var combinedStream = new this();
+
+ options = options || {};
+ for (var option in options) {
+ combinedStream[option] = options[option];
+ }
+
+ return combinedStream;
+};
+
+CombinedStream.isStreamLike = function(stream) {
+ return (typeof stream !== 'function')
+ && (typeof stream !== 'string')
+ && (typeof stream !== 'boolean')
+ && (typeof stream !== 'number')
+ && (!Buffer.isBuffer(stream));
+};
+
+CombinedStream.prototype.append = function(stream) {
+ var isStreamLike = CombinedStream.isStreamLike(stream);
+
+ if (isStreamLike) {
+ if (!(stream instanceof DelayedStream)) {
+ var newStream = DelayedStream.create(stream, {
+ maxDataSize: Infinity,
+ pauseStream: this.pauseStreams,
+ });
+ stream.on('data', this._checkDataSize.bind(this));
+ stream = newStream;
+ }
+
+ this._handleErrors(stream);
+
+ if (this.pauseStreams) {
+ stream.pause();
+ }
+ }
+
+ this._streams.push(stream);
+ return this;
+};
+
+CombinedStream.prototype.pipe = function(dest, options) {
+ Stream.prototype.pipe.call(this, dest, options);
+ this.resume();
+ return dest;
+};
+
+CombinedStream.prototype._getNext = function() {
+ this._currentStream = null;
+ var stream = this._streams.shift();
+
+
+ if (typeof stream == 'undefined') {
+ this.end();
+ return;
+ }
+
+ if (typeof stream !== 'function') {
+ this._pipeNext(stream);
+ return;
+ }
+
+ var getStream = stream;
+ getStream(function(stream) {
+ var isStreamLike = CombinedStream.isStreamLike(stream);
+ if (isStreamLike) {
+ stream.on('data', this._checkDataSize.bind(this));
+ this._handleErrors(stream);
+ }
+
+ this._pipeNext(stream);
+ }.bind(this));
+};
+
+CombinedStream.prototype._pipeNext = function(stream) {
+ this._currentStream = stream;
+
+ var isStreamLike = CombinedStream.isStreamLike(stream);
+ if (isStreamLike) {
+ stream.on('end', this._getNext.bind(this));
+ stream.pipe(this, {end: false});
+ return;
+ }
+
+ var value = stream;
+ this.write(value);
+ this._getNext();
+};
+
+CombinedStream.prototype._handleErrors = function(stream) {
+ var self = this;
+ stream.on('error', function(err) {
+ self._emitError(err);
+ });
+};
+
+CombinedStream.prototype.write = function(data) {
+ this.emit('data', data);
+};
+
+CombinedStream.prototype.pause = function() {
+ if (!this.pauseStreams) {
+ return;
+ }
+
+ if(this.pauseStreams && this._currentStream && typeof(this._currentStream.pause) == 'function') this._currentStream.pause();
+ this.emit('pause');
+};
+
+CombinedStream.prototype.resume = function() {
+ if (!this._released) {
+ this._released = true;
+ this.writable = true;
+ this._getNext();
+ }
+
+ if(this.pauseStreams && this._currentStream && typeof(this._currentStream.resume) == 'function') this._currentStream.resume();
+ this.emit('resume');
+};
+
+CombinedStream.prototype.end = function() {
+ this._reset();
+ this.emit('end');
+};
+
+CombinedStream.prototype.destroy = function() {
+ this._reset();
+ this.emit('close');
+};
+
+CombinedStream.prototype._reset = function() {
+ this.writable = false;
+ this._streams = [];
+ this._currentStream = null;
+};
+
+CombinedStream.prototype._checkDataSize = function() {
+ this._updateDataSize();
+ if (this.dataSize <= this.maxDataSize) {
+ return;
+ }
+
+ var message =
+ 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.';
+ this._emitError(new Error(message));
+};
+
+CombinedStream.prototype._updateDataSize = function() {
+ this.dataSize = 0;
+
+ var self = this;
+ this._streams.forEach(function(stream) {
+ if (!stream.dataSize) {
+ return;
+ }
+
+ self.dataSize += stream.dataSize;
+ });
+
+ if (this._currentStream && this._currentStream.dataSize) {
+ this.dataSize += this._currentStream.dataSize;
+ }
+};
+
+CombinedStream.prototype._emitError = function(err) {
+ this._reset();
+ this.emit('error', err);
+};
--- /dev/null
+*.un~
+/node_modules/*
--- /dev/null
+Copyright (c) 2011 Debuggable Limited <felix@debuggable.com>
+
+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.
--- /dev/null
+SHELL := /bin/bash
+
+test:
+ @./test/run.js
+
+.PHONY: test
+
--- /dev/null
+# delayed-stream
+
+Buffers events from a stream until you are ready to handle them.
+
+## Installation
+
+``` bash
+npm install delayed-stream
+```
+
+## Usage
+
+The following example shows how to write a http echo server that delays its
+response by 1000 ms.
+
+``` javascript
+var DelayedStream = require('delayed-stream');
+var http = require('http');
+
+http.createServer(function(req, res) {
+ var delayed = DelayedStream.create(req);
+
+ setTimeout(function() {
+ res.writeHead(200);
+ delayed.pipe(res);
+ }, 1000);
+});
+```
+
+If you are not using `Stream#pipe`, you can also manually release the buffered
+events by calling `delayedStream.resume()`:
+
+``` javascript
+var delayed = DelayedStream.create(req);
+
+setTimeout(function() {
+ // Emit all buffered events and resume underlaying source
+ delayed.resume();
+}, 1000);
+```
+
+## Implementation
+
+In order to use this meta stream properly, here are a few things you should
+know about the implementation.
+
+### Event Buffering / Proxying
+
+All events of the `source` stream are hijacked by overwriting the `source.emit`
+method. Until node implements a catch-all event listener, this is the only way.
+
+However, delayed-stream still continues to emit all events it captures on the
+`source`, regardless of whether you have released the delayed stream yet or
+not.
+
+Upon creation, delayed-stream captures all `source` events and stores them in
+an internal event buffer. Once `delayedStream.release()` is called, all
+buffered events are emitted on the `delayedStream`, and the event buffer is
+cleared. After that, delayed-stream merely acts as a proxy for the underlaying
+source.
+
+### Error handling
+
+Error events on `source` are buffered / proxied just like any other events.
+However, `delayedStream.create` attaches a no-op `'error'` listener to the
+`source`. This way you only have to handle errors on the `delayedStream`
+object, rather than in two places.
+
+### Buffer limits
+
+delayed-stream provides a `maxDataSize` property that can be used to limit
+the amount of data being buffered. In order to protect you from bad `source`
+streams that don't react to `source.pause()`, this feature is enabled by
+default.
+
+## API
+
+### DelayedStream.create(source, [options])
+
+Returns a new `delayedStream`. Available options are:
+
+* `pauseStream`
+* `maxDataSize`
+
+The description for those properties can be found below.
+
+### delayedStream.source
+
+The `source` stream managed by this object. This is useful if you are
+passing your `delayedStream` around, and you still want to access properties
+on the `source` object.
+
+### delayedStream.pauseStream = true
+
+Whether to pause the underlaying `source` when calling
+`DelayedStream.create()`. Modifying this property afterwards has no effect.
+
+### delayedStream.maxDataSize = 1024 * 1024
+
+The amount of data to buffer before emitting an `error`.
+
+If the underlaying source is emitting `Buffer` objects, the `maxDataSize`
+refers to bytes.
+
+If the underlaying source is emitting JavaScript strings, the size refers to
+characters.
+
+If you know what you are doing, you can set this property to `Infinity` to
+disable this feature. You can also modify this property during runtime.
+
+### delayedStream.maxDataSize = 1024 * 1024
+
+The amount of data to buffer before emitting an `error`.
+
+If the underlaying source is emitting `Buffer` objects, the `maxDataSize`
+refers to bytes.
+
+If the underlaying source is emitting JavaScript strings, the size refers to
+characters.
+
+If you know what you are doing, you can set this property to `Infinity` to
+disable this feature.
+
+### delayedStream.dataSize = 0
+
+The amount of data buffered so far.
+
+### delayedStream.readable
+
+An ECMA5 getter that returns the value of `source.readable`.
+
+### delayedStream.resume()
+
+If the `delayedStream` has not been released so far, `delayedStream.release()`
+is called.
+
+In either case, `source.resume()` is called.
+
+### delayedStream.pause()
+
+Calls `source.pause()`.
+
+### delayedStream.pipe(dest)
+
+Calls `delayedStream.resume()` and then proxies the arguments to `source.pipe`.
+
+### delayedStream.release()
+
+Emits and clears all events that have been buffered up so far. This does not
+resume the underlaying source, use `delayedStream.resume()` instead.
+
+## License
+
+delayed-stream is licensed under the MIT license.
--- /dev/null
+var Stream = require('stream').Stream;
+var util = require('util');
+
+module.exports = DelayedStream;
+function DelayedStream() {
+ this.source = null;
+ this.dataSize = 0;
+ this.maxDataSize = 1024 * 1024;
+ this.pauseStream = true;
+
+ this._maxDataSizeExceeded = false;
+ this._released = false;
+ this._bufferedEvents = [];
+}
+util.inherits(DelayedStream, Stream);
+
+DelayedStream.create = function(source, options) {
+ var delayedStream = new this();
+
+ options = options || {};
+ for (var option in options) {
+ delayedStream[option] = options[option];
+ }
+
+ delayedStream.source = source;
+
+ var realEmit = source.emit;
+ source.emit = function() {
+ delayedStream._handleEmit(arguments);
+ return realEmit.apply(source, arguments);
+ };
+
+ source.on('error', function() {});
+ if (delayedStream.pauseStream) {
+ source.pause();
+ }
+
+ return delayedStream;
+};
+
+DelayedStream.prototype.__defineGetter__('readable', function() {
+ return this.source.readable;
+});
+
+DelayedStream.prototype.resume = function() {
+ if (!this._released) {
+ this.release();
+ }
+
+ this.source.resume();
+};
+
+DelayedStream.prototype.pause = function() {
+ this.source.pause();
+};
+
+DelayedStream.prototype.release = function() {
+ this._released = true;
+
+ this._bufferedEvents.forEach(function(args) {
+ this.emit.apply(this, args);
+ }.bind(this));
+ this._bufferedEvents = [];
+};
+
+DelayedStream.prototype.pipe = function() {
+ var r = Stream.prototype.pipe.apply(this, arguments);
+ this.resume();
+ return r;
+};
+
+DelayedStream.prototype._handleEmit = function(args) {
+ if (this._released) {
+ this.emit.apply(this, args);
+ return;
+ }
+
+ if (args[0] === 'data') {
+ this.dataSize += args[1].length;
+ this._checkIfMaxDataSizeExceeded();
+ }
+
+ this._bufferedEvents.push(args);
+};
+
+DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() {
+ if (this._maxDataSizeExceeded) {
+ return;
+ }
+
+ if (this.dataSize <= this.maxDataSize) {
+ return;
+ }
+
+ this._maxDataSizeExceeded = true;
+ var message =
+ 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'
+ this.emit('error', new Error(message));
+};
--- /dev/null
+{
+ "author": {
+ "name": "Felix Geisendörfer",
+ "email": "felix@debuggable.com",
+ "url": "http://debuggable.com/"
+ },
+ "name": "delayed-stream",
+ "description": "Buffers events from a stream until you are ready to handle them.",
+ "version": "0.0.5",
+ "homepage": "https://github.com/felixge/node-delayed-stream",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/felixge/node-delayed-stream.git"
+ },
+ "main": "./lib/delayed_stream",
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "fake": "0.2.0",
+ "far": "0.0.1"
+ },
+ "_id": "delayed-stream@0.0.5",
+ "_engineSupported": true,
+ "_npmVersion": "1.0.3",
+ "_nodeVersion": "v0.4.9-pre",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "d4b1f43a93e8296dfe02694f4680bc37a313c73f",
+ "tarball": "http://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
+ },
+ "scripts": {},
+ "directories": {},
+ "_shasum": "d4b1f43a93e8296dfe02694f4680bc37a313c73f",
+ "_resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz",
+ "_from": "delayed-stream@0.0.5"
+}
--- /dev/null
+var common = module.exports;
+
+common.DelayedStream = require('..');
+common.assert = require('assert');
+common.fake = require('fake');
+common.PORT = 49252;
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var DelayedStream = common.DelayedStream;
+var http = require('http');
+
+var UPLOAD = new Buffer(10 * 1024 * 1024);
+
+var server = http.createServer(function(req, res) {
+ var delayed = DelayedStream.create(req, {maxDataSize: UPLOAD.length});
+
+ setTimeout(function() {
+ res.writeHead(200);
+ delayed.pipe(res);
+ }, 10);
+});
+server.listen(common.PORT, function() {
+ var request = http.request({
+ method: 'POST',
+ port: common.PORT,
+ });
+
+ request.write(UPLOAD);
+ request.end();
+
+ request.on('response', function(res) {
+ var received = 0;
+ res
+ .on('data', function(chunk) {
+ received += chunk.length;
+ })
+ .on('end', function() {
+ assert.equal(received, UPLOAD.length);
+ server.close();
+ });
+ });
+});
+
+
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testAutoPause() {
+ var source = new Stream();
+
+ fake.expect(source, 'pause', 1);
+ var delayedStream = DelayedStream.create(source);
+ fake.verify();
+})();
+
+(function testDisableAutoPause() {
+ var source = new Stream();
+ fake.expect(source, 'pause', 0);
+
+ var delayedStream = DelayedStream.create(source, {pauseStream: false});
+ fake.verify();
+})();
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testDelayEventsUntilResume() {
+ var source = new Stream();
+ var delayedStream = DelayedStream.create(source, {pauseStream: false});
+
+ fake.expect(source, 'pause');
+ delayedStream.pause();
+ fake.verify();
+})();
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testDelayEventsUntilResume() {
+ var source = new Stream();
+ var delayedStream = DelayedStream.create(source, {pauseStream: false});
+
+ // delayedStream must not emit until we resume
+ fake.expect(delayedStream, 'emit', 0);
+
+ // but our original source must emit
+ var params = [];
+ source.on('foo', function(param) {
+ params.push(param);
+ });
+
+ source.emit('foo', 1);
+ source.emit('foo', 2);
+
+ // Make sure delayedStream did not emit, and source did
+ assert.deepEqual(params, [1, 2]);
+ fake.verify();
+
+ // After resume, delayedStream must playback all events
+ fake
+ .stub(delayedStream, 'emit')
+ .times(Infinity)
+ .withArg(1, 'newListener');
+ fake.expect(delayedStream, 'emit', ['foo', 1]);
+ fake.expect(delayedStream, 'emit', ['foo', 2]);
+ fake.expect(source, 'resume');
+
+ delayedStream.resume();
+ fake.verify();
+
+ // Calling resume again will delegate to source
+ fake.expect(source, 'resume');
+ delayedStream.resume();
+ fake.verify();
+
+ // Emitting more events directly leads to them being emitted
+ fake.expect(delayedStream, 'emit', ['foo', 3]);
+ source.emit('foo', 3);
+ fake.verify();
+})();
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testHandleSourceErrors() {
+ var source = new Stream();
+ var delayedStream = DelayedStream.create(source, {pauseStream: false});
+
+ // We deal with this by attaching a no-op listener to 'error' on the source
+ // when creating a new DelayedStream. This way error events on the source
+ // won't throw.
+ source.emit('error', new Error('something went wrong'));
+})();
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testMaxDataSize() {
+ var source = new Stream();
+ var delayedStream = DelayedStream.create(source, {maxDataSize: 1024, pauseStream: false});
+
+ source.emit('data', new Buffer(1024));
+
+ fake
+ .expect(delayedStream, 'emit')
+ .withArg(1, 'error');
+ source.emit('data', new Buffer(1));
+ fake.verify();
+})();
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testPipeReleases() {
+ var source = new Stream();
+ var delayedStream = DelayedStream.create(source, {pauseStream: false});
+
+ fake.expect(delayedStream, 'resume');
+ delayedStream.pipe(new Stream());
+})();
--- /dev/null
+var common = require('../common');
+var assert = common.assert;
+var fake = common.fake.create();
+var DelayedStream = common.DelayedStream;
+var Stream = require('stream').Stream;
+
+(function testProxyReadableProperty() {
+ var source = new Stream();
+ var delayedStream = DelayedStream.create(source, {pauseStream: false});
+
+ source.readable = fake.value('source.readable');
+ assert.strictEqual(delayedStream.readable, source.readable);
+})();
--- /dev/null
+#!/usr/bin/env node
+var far = require('far').create();
+
+far.add(__dirname);
+far.include(/test-.*\.js$/);
+
+far.execute();
--- /dev/null
+{
+ "author": {
+ "name": "Felix Geisendörfer",
+ "email": "felix@debuggable.com",
+ "url": "http://debuggable.com/"
+ },
+ "name": "combined-stream",
+ "description": "A stream that emits multiple other streams one after another.",
+ "version": "0.0.7",
+ "homepage": "https://github.com/felixge/node-combined-stream",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/felixge/node-combined-stream.git"
+ },
+ "main": "./lib/combined_stream",
+ "scripts": {
+ "test": "node test/run.js"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "dependencies": {
+ "delayed-stream": "0.0.5"
+ },
+ "devDependencies": {
+ "far": "~0.0.7"
+ },
+ "bugs": {
+ "url": "https://github.com/felixge/node-combined-stream/issues"
+ },
+ "_id": "combined-stream@0.0.7",
+ "dist": {
+ "shasum": "0137e657baa5a7541c57ac37ac5fc07d73b4dc1f",
+ "tarball": "http://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz"
+ },
+ "_from": "combined-stream@>=0.0.5 <0.1.0",
+ "_npmVersion": "1.4.3",
+ "_npmUser": {
+ "name": "felixge",
+ "email": "felix@debuggable.com"
+ },
+ "maintainers": [
+ {
+ "name": "felixge",
+ "email": "felix@debuggable.com"
+ },
+ {
+ "name": "celer",
+ "email": "celer@scrypt.net"
+ },
+ {
+ "name": "alexindigo",
+ "email": "iam@alexindigo.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "0137e657baa5a7541c57ac37ac5fc07d73b4dc1f",
+ "_resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
--- /dev/null
+forever-agent
+=============
+
+HTTP Agent that keeps socket connections alive between keep-alive requests. Formerly part of mikeal/request, now a standalone module.
--- /dev/null
+module.exports = ForeverAgent
+ForeverAgent.SSL = ForeverAgentSSL
+
+var util = require('util')
+ , Agent = require('http').Agent
+ , net = require('net')
+ , tls = require('tls')
+ , AgentSSL = require('https').Agent
+
+function getConnectionName(host, port) {
+ var name = ''
+ if (typeof host === 'string') {
+ name = host + ':' + port
+ } else {
+ // For node.js v012.0 and iojs-v1.5.1, host is an object. And any existing localAddress is part of the connection name.
+ name = host.host + ':' + host.port + ':' + (host.localAddress ? (host.localAddress + ':') : ':')
+ }
+ return name
+}
+
+function ForeverAgent(options) {
+ var self = this
+ self.options = options || {}
+ self.requests = {}
+ self.sockets = {}
+ self.freeSockets = {}
+ self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets
+ self.minSockets = self.options.minSockets || ForeverAgent.defaultMinSockets
+ self.on('free', function(socket, host, port) {
+ var name = getConnectionName(host, port)
+
+ if (self.requests[name] && self.requests[name].length) {
+ self.requests[name].shift().onSocket(socket)
+ } else if (self.sockets[name].length < self.minSockets) {
+ if (!self.freeSockets[name]) self.freeSockets[name] = []
+ self.freeSockets[name].push(socket)
+
+ // if an error happens while we don't use the socket anyway, meh, throw the socket away
+ var onIdleError = function() {
+ socket.destroy()
+ }
+ socket._onIdleError = onIdleError
+ socket.on('error', onIdleError)
+ } else {
+ // If there are no pending requests just destroy the
+ // socket and it will get removed from the pool. This
+ // gets us out of timeout issues and allows us to
+ // default to Connection:keep-alive.
+ socket.destroy()
+ }
+ })
+
+}
+util.inherits(ForeverAgent, Agent)
+
+ForeverAgent.defaultMinSockets = 5
+
+
+ForeverAgent.prototype.createConnection = net.createConnection
+ForeverAgent.prototype.addRequestNoreuse = Agent.prototype.addRequest
+ForeverAgent.prototype.addRequest = function(req, host, port) {
+ var name = getConnectionName(host, port)
+
+ if (typeof host !== 'string') {
+ var options = host
+ port = options.port
+ host = options.host
+ }
+
+ if (this.freeSockets[name] && this.freeSockets[name].length > 0 && !req.useChunkedEncodingByDefault) {
+ var idleSocket = this.freeSockets[name].pop()
+ idleSocket.removeListener('error', idleSocket._onIdleError)
+ delete idleSocket._onIdleError
+ req._reusedSocket = true
+ req.onSocket(idleSocket)
+ } else {
+ this.addRequestNoreuse(req, host, port)
+ }
+}
+
+ForeverAgent.prototype.removeSocket = function(s, name, host, port) {
+ if (this.sockets[name]) {
+ var index = this.sockets[name].indexOf(s)
+ if (index !== -1) {
+ this.sockets[name].splice(index, 1)
+ }
+ } else if (this.sockets[name] && this.sockets[name].length === 0) {
+ // don't leak
+ delete this.sockets[name]
+ delete this.requests[name]
+ }
+
+ if (this.freeSockets[name]) {
+ var index = this.freeSockets[name].indexOf(s)
+ if (index !== -1) {
+ this.freeSockets[name].splice(index, 1)
+ if (this.freeSockets[name].length === 0) {
+ delete this.freeSockets[name]
+ }
+ }
+ }
+
+ if (this.requests[name] && this.requests[name].length) {
+ // If we have pending requests and a socket gets closed a new one
+ // needs to be created to take over in the pool for the one that closed.
+ this.createSocket(name, host, port).emit('free')
+ }
+}
+
+function ForeverAgentSSL (options) {
+ ForeverAgent.call(this, options)
+}
+util.inherits(ForeverAgentSSL, ForeverAgent)
+
+ForeverAgentSSL.prototype.createConnection = createConnectionSSL
+ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest
+
+function createConnectionSSL (port, host, options) {
+ if (typeof port === 'object') {
+ options = port;
+ } else if (typeof host === 'object') {
+ options = host;
+ } else if (typeof options === 'object') {
+ options = options;
+ } else {
+ options = {};
+ }
+
+ if (typeof port === 'number') {
+ options.port = port;
+ }
+
+ if (typeof host === 'string') {
+ options.host = host;
+ }
+
+ return tls.connect(options);
+}
--- /dev/null
+{
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com",
+ "url": "http://www.futurealoof.com"
+ },
+ "name": "forever-agent",
+ "description": "HTTP Agent that keeps socket connections alive between keep-alive requests. Formerly part of mikeal/request, now a standalone module.",
+ "version": "0.6.1",
+ "license": "Apache-2.0",
+ "repository": {
+ "url": "https://github.com/mikeal/forever-agent"
+ },
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "gitHead": "1b3b6163f2b3c2c4122bbfa288c1325c0df9871d",
+ "bugs": {
+ "url": "https://github.com/mikeal/forever-agent/issues"
+ },
+ "homepage": "https://github.com/mikeal/forever-agent",
+ "_id": "forever-agent@0.6.1",
+ "scripts": {},
+ "_shasum": "fbc71f0c41adeb37f96c577ad1ed42d8fdacca91",
+ "_from": "forever-agent@>=0.6.0 <0.7.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "simov",
+ "email": "simeonvelichkov@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ {
+ "name": "nylen",
+ "email": "jnylen@gmail.com"
+ },
+ {
+ "name": "simov",
+ "email": "simeonvelichkov@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "fbc71f0c41adeb37f96c577ad1ed42d8fdacca91",
+ "tarball": "http://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
+}
--- /dev/null
+Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and 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.
--- /dev/null
+# Form-Data [](https://travis-ci.org/felixge/node-form-data) [](https://gemnasium.com/felixge/node-form-data)
+
+A module to create readable ```"multipart/form-data"``` streams. Can be used to submit forms and file uploads to other web applications.
+
+The API of this module is inspired by the [XMLHttpRequest-2 FormData Interface][xhr2-fd].
+
+[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface
+[streams2-thing]: http://nodejs.org/api/stream.html#stream_compatibility_with_older_node_versions
+
+## Install
+
+```
+npm install form-data
+```
+
+## Usage
+
+In this example we are constructing a form with 3 fields that contain a string,
+a buffer and a file stream.
+
+``` javascript
+var FormData = require('form-data');
+var fs = require('fs');
+
+var form = new FormData();
+form.append('my_field', 'my value');
+form.append('my_buffer', new Buffer(10));
+form.append('my_file', fs.createReadStream('/foo/bar.jpg'));
+```
+
+Also you can use http-response stream:
+
+``` javascript
+var FormData = require('form-data');
+var http = require('http');
+
+var form = new FormData();
+
+http.request('http://nodejs.org/images/logo.png', function(response) {
+ form.append('my_field', 'my value');
+ form.append('my_buffer', new Buffer(10));
+ form.append('my_logo', response);
+});
+```
+
+Or @mikeal's request stream:
+
+``` javascript
+var FormData = require('form-data');
+var request = require('request');
+
+var form = new FormData();
+
+form.append('my_field', 'my value');
+form.append('my_buffer', new Buffer(10));
+form.append('my_logo', request('http://nodejs.org/images/logo.png'));
+```
+
+In order to submit this form to a web application, call ```submit(url, [callback])``` method:
+
+``` javascript
+form.submit('http://example.org/', function(err, res) {
+ // res – response object (http.IncomingMessage) //
+ res.resume(); // for node-0.10.x
+});
+
+```
+
+For more advanced request manipulations ```submit()``` method returns ```http.ClientRequest``` object, or you can choose from one of the alternative submission methods.
+
+### Alternative submission methods
+
+You can use node's http client interface:
+
+``` javascript
+var http = require('http');
+
+var request = http.request({
+ method: 'post',
+ host: 'example.org',
+ path: '/upload',
+ headers: form.getHeaders()
+});
+
+form.pipe(request);
+
+request.on('response', function(res) {
+ console.log(res.statusCode);
+});
+```
+
+Or if you would prefer the `'Content-Length'` header to be set for you:
+
+``` javascript
+form.submit('example.org/upload', function(err, res) {
+ console.log(res.statusCode);
+});
+```
+
+To use custom headers and pre-known length in parts:
+
+``` javascript
+var CRLF = '\r\n';
+var form = new FormData();
+
+var options = {
+ header: CRLF + '--' + form.getBoundary() + CRLF + 'X-Custom-Header: 123' + CRLF + CRLF,
+ knownLength: 1
+};
+
+form.append('my_buffer', buffer, options);
+
+form.submit('http://example.com/', function(err, res) {
+ if (err) throw err;
+ console.log('Done');
+});
+```
+
+Form-Data can recognize and fetch all the required information from common types of streams (```fs.readStream```, ```http.response``` and ```mikeal's request```), for some other types of streams you'd need to provide "file"-related information manually:
+
+``` javascript
+someModule.stream(function(err, stdout, stderr) {
+ if (err) throw err;
+
+ var form = new FormData();
+
+ form.append('file', stdout, {
+ filename: 'unicycle.jpg',
+ contentType: 'image/jpg',
+ knownLength: 19806
+ });
+
+ form.submit('http://example.com/', function(err, res) {
+ if (err) throw err;
+ console.log('Done');
+ });
+});
+```
+
+For edge cases, like POST request to URL with query string or to pass HTTP auth credentials, object can be passed to `form.submit()` as first parameter:
+
+``` javascript
+form.submit({
+ host: 'example.com',
+ path: '/probably.php?extra=params',
+ auth: 'username:password'
+}, function(err, res) {
+ console.log(res.statusCode);
+});
+```
+
+In case you need to also send custom HTTP headers with the POST request, you can use the `headers` key in first parameter of `form.submit()`:
+
+``` javascript
+form.submit({
+ host: 'example.com',
+ path: '/surelynot.php',
+ headers: {'x-test-header': 'test-header-value'}
+}, function(err, res) {
+ console.log(res.statusCode);
+});
+```
+
+## Notes
+
+- ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround.
+- If it feels like FormData hangs after submit and you're on ```node-0.10```, please check [Compatibility with Older Node Versions][streams2-thing]
+
+## TODO
+
+- Add new streams (0.10) support and try really hard not to break it for 0.8.x.
+
+## License
+
+Form-Data is licensed under the MIT license.
--- /dev/null
+var CombinedStream = require('combined-stream');
+var util = require('util');
+var path = require('path');
+var http = require('http');
+var https = require('https');
+var parseUrl = require('url').parse;
+var fs = require('fs');
+var mime = require('mime-types');
+var async = require('async');
+
+module.exports = FormData;
+function FormData() {
+ this._overheadLength = 0;
+ this._valueLength = 0;
+ this._lengthRetrievers = [];
+
+ CombinedStream.call(this);
+}
+util.inherits(FormData, CombinedStream);
+
+FormData.LINE_BREAK = '\r\n';
+
+FormData.prototype.append = function(field, value, options) {
+ options = options || {};
+
+ var append = CombinedStream.prototype.append.bind(this);
+
+ // all that streamy business can't handle numbers
+ if (typeof value == 'number') value = ''+value;
+
+ // https://github.com/felixge/node-form-data/issues/38
+ if (util.isArray(value)) {
+ // Please convert your array into string
+ // the way web server expects it
+ this._error(new Error('Arrays are not supported.'));
+ return;
+ }
+
+ var header = this._multiPartHeader(field, value, options);
+ var footer = this._multiPartFooter(field, value, options);
+
+ append(header);
+ append(value);
+ append(footer);
+
+ // pass along options.knownLength
+ this._trackLength(header, value, options);
+};
+
+FormData.prototype._trackLength = function(header, value, options) {
+ var valueLength = 0;
+
+ // used w/ getLengthSync(), when length is known.
+ // e.g. for streaming directly from a remote server,
+ // w/ a known file a size, and not wanting to wait for
+ // incoming file to finish to get its size.
+ if (options.knownLength != null) {
+ valueLength += +options.knownLength;
+ } else if (Buffer.isBuffer(value)) {
+ valueLength = value.length;
+ } else if (typeof value === 'string') {
+ valueLength = Buffer.byteLength(value);
+ }
+
+ this._valueLength += valueLength;
+
+ // @check why add CRLF? does this account for custom/multiple CRLFs?
+ this._overheadLength +=
+ Buffer.byteLength(header) +
+ + FormData.LINE_BREAK.length;
+
+ // empty or either doesn't have path or not an http response
+ if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) {
+ return;
+ }
+
+ // no need to bother with the length
+ if (!options.knownLength)
+ this._lengthRetrievers.push(function(next) {
+
+ if (value.hasOwnProperty('fd')) {
+
+ // take read range into a account
+ // `end` = Infinity –> read file till the end
+ //
+ // TODO: Looks like there is bug in Node fs.createReadStream
+ // it doesn't respect `end` options without `start` options
+ // Fix it when node fixes it.
+ // https://github.com/joyent/node/issues/7819
+ if (value.end != undefined && value.end != Infinity && value.start != undefined) {
+
+ // when end specified
+ // no need to calculate range
+ // inclusive, starts with 0
+ next(null, value.end+1 - (value.start ? value.start : 0));
+
+ // not that fast snoopy
+ } else {
+ // still need to fetch file size from fs
+ fs.stat(value.path, function(err, stat) {
+
+ var fileSize;
+
+ if (err) {
+ next(err);
+ return;
+ }
+
+ // update final size based on the range options
+ fileSize = stat.size - (value.start ? value.start : 0);
+ next(null, fileSize);
+ });
+ }
+
+ // or http response
+ } else if (value.hasOwnProperty('httpVersion')) {
+ next(null, +value.headers['content-length']);
+
+ // or request stream http://github.com/mikeal/request
+ } else if (value.hasOwnProperty('httpModule')) {
+ // wait till response come back
+ value.on('response', function(response) {
+ value.pause();
+ next(null, +response.headers['content-length']);
+ });
+ value.resume();
+
+ // something else
+ } else {
+ next('Unknown stream');
+ }
+ });
+};
+
+FormData.prototype._multiPartHeader = function(field, value, options) {
+ var boundary = this.getBoundary();
+ var header = '';
+
+ // custom header specified (as string)?
+ // it becomes responsible for boundary
+ // (e.g. to handle extra CRLFs on .NET servers)
+ if (options.header != null) {
+ header = options.header;
+ } else {
+ header += '--' + boundary + FormData.LINE_BREAK +
+ 'Content-Disposition: form-data; name="' + field + '"';
+
+ // fs- and request- streams have path property
+ // or use custom filename and/or contentType
+ // TODO: Use request's response mime-type
+ if (options.filename || value.path) {
+ header +=
+ '; filename="' + path.basename(options.filename || value.path) + '"' + FormData.LINE_BREAK +
+ 'Content-Type: ' + (options.contentType || mime.lookup(options.filename || value.path));
+
+ // http response has not
+ } else if (value.readable && value.hasOwnProperty('httpVersion')) {
+ header +=
+ '; filename="' + path.basename(value.client._httpMessage.path) + '"' + FormData.LINE_BREAK +
+ 'Content-Type: ' + value.headers['content-type'];
+ }
+
+ header += FormData.LINE_BREAK + FormData.LINE_BREAK;
+ }
+
+ return header;
+};
+
+FormData.prototype._multiPartFooter = function(field, value, options) {
+ return function(next) {
+ var footer = FormData.LINE_BREAK;
+
+ var lastPart = (this._streams.length === 0);
+ if (lastPart) {
+ footer += this._lastBoundary();
+ }
+
+ next(footer);
+ }.bind(this);
+};
+
+FormData.prototype._lastBoundary = function() {
+ return '--' + this.getBoundary() + '--';
+};
+
+FormData.prototype.getHeaders = function(userHeaders) {
+ var formHeaders = {
+ 'content-type': 'multipart/form-data; boundary=' + this.getBoundary()
+ };
+
+ for (var header in userHeaders) {
+ formHeaders[header.toLowerCase()] = userHeaders[header];
+ }
+
+ return formHeaders;
+}
+
+FormData.prototype.getCustomHeaders = function(contentType) {
+ contentType = contentType ? contentType : 'multipart/form-data';
+
+ var formHeaders = {
+ 'content-type': contentType + '; boundary=' + this.getBoundary(),
+ 'content-length': this.getLengthSync()
+ };
+
+ return formHeaders;
+}
+
+FormData.prototype.getBoundary = function() {
+ if (!this._boundary) {
+ this._generateBoundary();
+ }
+
+ return this._boundary;
+};
+
+FormData.prototype._generateBoundary = function() {
+ // This generates a 50 character boundary similar to those used by Firefox.
+ // They are optimized for boyer-moore parsing.
+ var boundary = '--------------------------';
+ for (var i = 0; i < 24; i++) {
+ boundary += Math.floor(Math.random() * 10).toString(16);
+ }
+
+ this._boundary = boundary;
+};
+
+// Note: getLengthSync DOESN'T calculate streams length
+// As workaround one can calculate file size manually
+// and add it as knownLength option
+FormData.prototype.getLengthSync = function(debug) {
+ var knownLength = this._overheadLength + this._valueLength;
+
+ // Don't get confused, there are 3 "internal" streams for each keyval pair
+ // so it basically checks if there is any value added to the form
+ if (this._streams.length) {
+ knownLength += this._lastBoundary().length;
+ }
+
+ // https://github.com/felixge/node-form-data/issues/40
+ if (this._lengthRetrievers.length) {
+ // Some async length retrivers are present
+ // therefore synchronous length calculation is false.
+ // Please use getLength(callback) to get proper length
+ this._error(new Error('Cannot calculate proper length in synchronous way.'));
+ }
+
+ return knownLength;
+};
+
+FormData.prototype.getLength = function(cb) {
+ var knownLength = this._overheadLength + this._valueLength;
+
+ if (this._streams.length) {
+ knownLength += this._lastBoundary().length;
+ }
+
+ if (!this._lengthRetrievers.length) {
+ process.nextTick(cb.bind(this, null, knownLength));
+ return;
+ }
+
+ async.parallel(this._lengthRetrievers, function(err, values) {
+ if (err) {
+ cb(err);
+ return;
+ }
+
+ values.forEach(function(length) {
+ knownLength += length;
+ });
+
+ cb(null, knownLength);
+ });
+};
+
+FormData.prototype.submit = function(params, cb) {
+
+ var request
+ , options
+ , defaults = {
+ method : 'post'
+ };
+
+ // parse provided url if it's string
+ // or treat it as options object
+ if (typeof params == 'string') {
+ params = parseUrl(params);
+
+ options = populate({
+ port: params.port,
+ path: params.pathname,
+ host: params.hostname
+ }, defaults);
+ }
+ else // use custom params
+ {
+ options = populate(params, defaults);
+ // if no port provided use default one
+ if (!options.port) {
+ options.port = options.protocol == 'https:' ? 443 : 80;
+ }
+ }
+
+ // put that good code in getHeaders to some use
+ options.headers = this.getHeaders(params.headers);
+
+ // https if specified, fallback to http in any other case
+ if (params.protocol == 'https:') {
+ request = https.request(options);
+ } else {
+ request = http.request(options);
+ }
+
+ // get content length and fire away
+ this.getLength(function(err, length) {
+
+ // TODO: Add chunked encoding when no length (if err)
+
+ // add content length
+ request.setHeader('Content-Length', length);
+
+ this.pipe(request);
+ if (cb) {
+ request.on('error', cb);
+ request.on('response', cb.bind(this, null));
+ }
+ }.bind(this));
+
+ return request;
+};
+
+FormData.prototype._error = function(err) {
+ if (this.error) return;
+
+ this.error = err;
+ this.pause();
+ this.emit('error', err);
+};
+
+/*
+ * Santa's little helpers
+ */
+
+// populates missing values
+function populate(dst, src) {
+ for (var prop in src) {
+ if (!dst[prop]) dst[prop] = src[prop];
+ }
+ return dst;
+}
--- /dev/null
+{
+ "author": {
+ "name": "Felix Geisendörfer",
+ "email": "felix@debuggable.com",
+ "url": "http://debuggable.com/"
+ },
+ "name": "form-data",
+ "description": "A module to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.",
+ "version": "0.2.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/felixge/node-form-data.git"
+ },
+ "main": "./lib/form_data",
+ "scripts": {
+ "test": "node test/run.js"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "dependencies": {
+ "async": "~0.9.0",
+ "combined-stream": "~0.0.4",
+ "mime-types": "~2.0.3"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://raw.github.com/felixge/node-form-data/master/License"
+ }
+ ],
+ "devDependencies": {
+ "fake": "~0.2.2",
+ "far": "~0.0.7",
+ "formidable": "~1.0.14",
+ "request": "~2.36.0"
+ },
+ "gitHead": "dfc1a2aef40b97807e2ffe477da06cb2c37e259f",
+ "bugs": {
+ "url": "https://github.com/felixge/node-form-data/issues"
+ },
+ "homepage": "https://github.com/felixge/node-form-data",
+ "_id": "form-data@0.2.0",
+ "_shasum": "26f8bc26da6440e299cbdcfb69035c4f77a6e466",
+ "_from": "form-data@>=0.2.0 <0.3.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "alexindigo",
+ "email": "iam@alexindigo.com"
+ },
+ "maintainers": [
+ {
+ "name": "felixge",
+ "email": "felix@debuggable.com"
+ },
+ {
+ "name": "idralyuk",
+ "email": "igor@buran.us"
+ },
+ {
+ "name": "alexindigo",
+ "email": "iam@alexindigo.com"
+ },
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ {
+ "name": "celer",
+ "email": "dtyree77@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "26f8bc26da6440e299cbdcfb69035c4f77a6e466",
+ "tarball": "http://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2015 Ahmad Nassri (https://www.ahmadnassri.com)
+
+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.
--- /dev/null
+# HAR Validator [![version][npm-version]][npm-url] [![License][npm-license]][license-url]
+
+Extremely fast HTTP Archive ([HAR](http://www.softwareishard.com/blog/har-12-spec/)) validator using JSON Schema.
+
+[![Build Status][travis-image]][travis-url]
+[![Downloads][npm-downloads]][npm-url]
+[![Code Climate][codeclimate-quality]][codeclimate-url]
+[![Coverage Status][codeclimate-coverage]][codeclimate-url]
+[![Dependencies][david-image]][david-url]
+
+## Install
+
+```shell
+# to use in cli
+npm install --global har-validator
+
+# to use as a module
+npm install --save har-validator
+```
+
+## Usage
+
+```
+
+ Usage: har-validator [options] <files ...>
+
+ Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -s, --schema [name] validate schema name (log, request, response, etc ...)
+
+```
+
+###### Example
+
+```shell
+har-validator har.json
+
+har-validator --schema request request.json
+```
+
+## API
+
+### Validate(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a full [HAR](http://www.softwareishard.com/blog/har-12-spec/) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var HAR = require('./har.json');
+var validate = require('har-validator');
+
+validate(HAR, function (e, valid) {
+ if (e) console.log(e.errors)
+
+ if (valid) console.log('horray!');
+});
+```
+
+### Validate.log(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [log](http://www.softwareishard.com/blog/har-12-spec/#log) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.log(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.cache(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [cache](http://www.softwareishard.com/blog/har-12-spec/#cache) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.cache(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.cacheEntry(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a ["beforeRequest" or "afterRequest"](http://www.softwareishard.com/blog/har-12-spec/#cache) objects
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.cacheEntry(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.content(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [content](http://www.softwareishard.com/blog/har-12-spec/#content) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.content(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.cookie(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [cookie](http://www.softwareishard.com/blog/har-12-spec/#cookies) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.cookie(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.creator(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [creator](http://www.softwareishard.com/blog/har-12-spec/#creator) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.creator(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.entry(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [entry](http://www.softwareishard.com/blog/har-12-spec/#entries) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.entry(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.log(data [, callback])
+
+alias of [`Validate(data [, callback])`](#validate-data-callback-)
+
+### Validate.page(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [page](http://www.softwareishard.com/blog/har-12-spec/#pages) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.page(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.pageTimings(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [pageTimings](http://www.softwareishard.com/blog/har-12-spec/#pageTimings) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.pageTimings(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.postData(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [postData](http://www.softwareishard.com/blog/har-12-spec/#postData) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.postData(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.record(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [record](http://www.softwareishard.com/blog/har-12-spec/#headers) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.record(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.request(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [request](http://www.softwareishard.com/blog/har-12-spec/#request) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.request(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.response(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [response](http://www.softwareishard.com/blog/har-12-spec/#response) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.cacheEntry(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+### Validate.timings(data [, callback])
+
+Returns `true` or `false`.
+
+- **data**: `Object` *(Required)*
+ a [timings](http://www.softwareishard.com/blog/har-12-spec/#timings) object
+
+- **callback**: `Function`
+ gets two arguments (err, valid)
+
+```js
+var validate = require('har-validator');
+
+validate.timings(data, function (e, valid) {
+ if (e) console.log(e.errors)
+});
+```
+
+## Support
+
+Donations are welcome to help support the continuous development of this project.
+
+[![Gratipay][gratipay-image]][gratipay-url]
+[![PayPal][paypal-image]][paypal-url]
+[![Flattr][flattr-image]][flattr-url]
+[![Bitcoin][bitcoin-image]][bitcoin-url]
+
+## License
+
+[MIT](LICENSE) © [Ahmad Nassri](https://www.ahmadnassri.com)
+
+[license-url]: https://github.com/ahmadnassri/har-validator/blob/master/LICENSE
+
+[travis-url]: https://travis-ci.org/ahmadnassri/har-validator
+[travis-image]: https://img.shields.io/travis/ahmadnassri/har-validator.svg?style=flat-square
+
+[npm-url]: https://www.npmjs.com/package/har-validator
+[npm-license]: https://img.shields.io/npm/l/har-validator.svg?style=flat-square
+[npm-version]: https://img.shields.io/npm/v/har-validator.svg?style=flat-square
+[npm-downloads]: https://img.shields.io/npm/dm/har-validator.svg?style=flat-square
+
+[codeclimate-url]: https://codeclimate.com/github/ahmadnassri/har-validator
+[codeclimate-quality]: https://img.shields.io/codeclimate/github/ahmadnassri/har-validator.svg?style=flat-square
+[codeclimate-coverage]: https://img.shields.io/codeclimate/coverage/github/ahmadnassri/har-validator.svg?style=flat-square
+
+[david-url]: https://david-dm.org/ahmadnassri/har-validator
+[david-image]: https://img.shields.io/david/ahmadnassri/har-validator.svg?style=flat-square
+
+[gratipay-url]: https://www.gratipay.com/ahmadnassri/
+[gratipay-image]: https://img.shields.io/gratipay/ahmadnassri.svg?style=flat-square
+
+[paypal-url]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UJ2B2BTK9VLRS&on0=project&os0=har-validator
+[paypal-image]: http://img.shields.io/badge/paypal-donate-green.svg?style=flat-square
+
+[flattr-url]: https://flattr.com/submit/auto?user_id=ahmadnassri&url=https://github.com/ahmadnassri/har-validator&title=har-validator&language=&tags=github&category=software
+[flattr-image]: http://img.shields.io/badge/flattr-donate-green.svg?style=flat-square
+
+[bitcoin-image]: http://img.shields.io/badge/bitcoin-1Nb46sZRVG3or7pNaDjthcGJpWhvoPpCxy-green.svg?style=flat-square
+[bitcoin-url]: https://www.coinbase.com/checkouts/ae383ae6bb931a2fa5ad11cec115191e?name=har-validator
--- /dev/null
+#!/usr/bin/env node
+
+'use strict'
+
+var Promise = require('bluebird')
+
+var chalk = require('chalk')
+var cmd = require('commander')
+var fs = Promise.promisifyAll(require('fs'))
+var path = require('path')
+var pkg = require('../package.json')
+var validate = Promise.promisifyAll(require('..'))
+
+cmd
+ .version(pkg.version)
+ .usage('[options] <files ...>')
+ .option('-s, --schema [name]', 'validate schema name (log, request, response, etc ...)')
+ .parse(process.argv)
+
+if (!cmd.args.length) {
+ cmd.help()
+}
+
+if (!cmd.schema) {
+ cmd.schema = 'har'
+}
+
+cmd.args.map(function (fileName) {
+ var file = chalk.yellow.italic(path.basename(fileName))
+
+ fs.readFileAsync(fileName)
+ .then(JSON.parse)
+ .then(validate[cmd.schema + 'Async'])
+ .then(function () {
+ console.log('%s [%s] is valid', chalk.green('✓'), file)
+ })
+ .catch(SyntaxError, function (e) {
+ console.error('%s [%s] failed to read JSON: %s', chalk.red('✖'), file, chalk.red(e.message))
+ })
+ .catch(function (e) {
+ e.errors.map(function (err) {
+ console.error('%s [%s] failed validation: (%s: %s) %s', chalk.red('✖'), file, chalk.cyan.italic(err.field), chalk.magenta.italic(err.value), chalk.red(err.message))
+ })
+ })
+})
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2014 Petka Antonov
+
+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:</p>
+
+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.
--- /dev/null
+<a href="http://promisesaplus.com/">
+ <img src="http://promisesaplus.com/assets/logo-small.png" alt="Promises/A+ logo"
+ title="Promises/A+ 1.1 compliant" align="right" />
+</a>
+[](https://travis-ci.org/petkaantonov/bluebird)
+[](http://petkaantonov.github.io/bluebird/coverage/debug/index.html)
+
+
+# Introduction
+
+Bluebird is a fully featured [promise](#what-are-promises-and-why-should-i-use-them) library with focus on innovative features and performance
+
+
+
+# Topics
+
+- [Features](#features)
+- [Quick start](#quick-start)
+- [API Reference and examples](API.md)
+- [Support](#support)
+- [What are promises and why should I use them?](#what-are-promises-and-why-should-i-use-them)
+- [Questions and issues](#questions-and-issues)
+- [Error handling](#error-handling)
+- [Development](#development)
+ - [Testing](#testing)
+ - [Benchmarking](#benchmarks)
+ - [Custom builds](#custom-builds)
+ - [For library authors](#for-library-authors)
+- [What is the sync build?](#what-is-the-sync-build)
+- [License](#license)
+- [Snippets for common problems](https://github.com/petkaantonov/bluebird/wiki/Snippets)
+- [Promise anti-patterns](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns)
+- [Changelog](changelog.md)
+- [Optimization guide](#optimization-guide)
+
+# Features
+<img src="http://petkaantonov.github.io/bluebird/logo.png" alt="bluebird logo" align="right" />
+
+- [Promises A+](http://promisesaplus.com)
+- [Synchronous inspection](API.md#synchronous-inspection)
+- [Concurrency coordination](API.md#collections)
+- [Promisification on steroids](API.md#promisification)
+- [Resource management through a parallel of python `with`/C# `using`](API.md#resource-management)
+- [Cancellation and timeouts](API.md#cancellation)
+- [Parallel for C# `async` and `await`](API.md#generators)
+- Mind blowing utilities such as
+ - [`.bind()`](API.md#binddynamic-thisarg---promise)
+ - [`.call()`](API.md#callstring-propertyname--dynamic-arg---promise)
+ - [`Promise.join()`](API.md#promisejoinpromisethenablevalue-promises-function-handler---promise)
+ - [And](API.md#core) [much](API.md#timers) [more](API.md#utility)!
+- [Practical debugging solutions and sane defaults](#error-handling)
+- [Sick performance](benchmark/)
+
+<hr>
+
+# Quick start
+
+## Node.js
+
+ npm install bluebird
+
+Then:
+
+```js
+var Promise = require("bluebird");
+```
+
+## Browsers
+
+There are many ways to use bluebird in browsers:
+
+- Direct downloads
+ - Full build [bluebird.js](https://cdn.jsdelivr.net/bluebird/latest/bluebird.js)
+ - Full build minified [bluebird.min.js](https://cdn.jsdelivr.net/bluebird/latest/bluebird.min.js)
+- You may use browserify on the main export
+- You may use the [bower](http://bower.io) package.
+
+When using script tags the global variables `Promise` and `P` (alias for `Promise`) become available.
+
+A [minimal bluebird browser build](#custom-builds) is ≈38.92KB minified*, 11.65KB gzipped and has no external dependencies.
+
+*Google Closure Compiler using Simple.
+
+#### Browser support
+
+Browsers that [implement ECMA-262, edition 3](http://en.wikipedia.org/wiki/Ecmascript#Implementations) and later are supported.
+
+[](https://saucelabs.com/u/petka_antonov)
+
+**Note** that in ECMA-262, edition 3 (IE7, IE8 etc.) it is not possible to use methods that have keyword names like `.catch` and `.finally`. The [API documentation](API.md) always lists a compatible alternative name that you can use if you need to support these browsers. For example `.catch` is replaced with `.caught` and `.finally` with `.lastly`.
+
+Also, [long stack trace](API.md#promiselongstacktraces---void) support is only available in Chrome, Firefox and Internet Explorer 10+.
+
+After quick start, see [API Reference and examples](API.md)
+
+<hr>
+
+# Support
+
+- Mailing list: [bluebird-js@googlegroups.com](https://groups.google.com/forum/#!forum/bluebird-js)
+- IRC: #promises @freenode
+- StackOverflow: [bluebird tag](http://stackoverflow.com/questions/tagged/bluebird)
+- Bugs and feature requests: [github issue tracker](https://github.com/petkaantonov/bluebird/issues?state=open)
+
+<hr>
+
+# What are promises and why should I use them?
+
+You should use promises to turn this:
+
+```js
+fs.readFile("file.json", function(err, val) {
+ if( err ) {
+ console.error("unable to read file");
+ }
+ else {
+ try {
+ val = JSON.parse(val);
+ console.log(val.success);
+ }
+ catch( e ) {
+ console.error("invalid json in file");
+ }
+ }
+});
+```
+
+Into this:
+
+```js
+fs.readFileAsync("file.json").then(JSON.parse).then(function(val) {
+ console.log(val.success);
+})
+.catch(SyntaxError, function(e) {
+ console.error("invalid json in file");
+})
+.catch(function(e) {
+ console.error("unable to read file")
+});
+```
+
+*If you are wondering "there is no `readFileAsync` method on `fs` that returns a promise", see [promisification](API.md#promisification)*
+
+Actually you might notice the latter has a lot in common with code that would do the same using synchronous I/O:
+
+```js
+try {
+ var val = JSON.parse(fs.readFileSync("file.json"));
+ console.log(val.success);
+}
+//Syntax actually not supported in JS but drives the point
+catch(SyntaxError e) {
+ console.error("invalid json in file");
+}
+catch(Error e) {
+ console.error("unable to read file")
+}
+```
+
+And that is the point - being able to have something that is a lot like `return` and `throw` in synchronous code.
+
+You can also use promises to improve code that was written with callback helpers:
+
+
+```js
+//Copyright Plato http://stackoverflow.com/a/19385911/995876
+//CC BY-SA 2.5
+mapSeries(URLs, function (URL, done) {
+ var options = {};
+ needle.get(URL, options, function (error, response, body) {
+ if (error) {
+ return done(error)
+ }
+ try {
+ var ret = JSON.parse(body);
+ return done(null, ret);
+ }
+ catch (e) {
+ done(e);
+ }
+ });
+}, function (err, results) {
+ if (err) {
+ console.log(err)
+ } else {
+ console.log('All Needle requests successful');
+ // results is a 1 to 1 mapping in order of URLs > needle.body
+ processAndSaveAllInDB(results, function (err) {
+ if (err) {
+ return done(err)
+ }
+ console.log('All Needle requests saved');
+ done(null);
+ });
+ }
+});
+```
+
+Is more pleasing to the eye when done with promises:
+
+```js
+Promise.promisifyAll(needle);
+var options = {};
+
+var current = Promise.resolve();
+Promise.map(URLs, function(URL) {
+ current = current.then(function () {
+ return needle.getAsync(URL, options);
+ });
+ return current;
+}).map(function(responseAndBody){
+ return JSON.parse(responseAndBody[1]);
+}).then(function (results) {
+ return processAndSaveAllInDB(results);
+}).then(function(){
+ console.log('All Needle requests saved');
+}).catch(function (e) {
+ console.log(e);
+});
+```
+
+Also promises don't just give you correspondences for synchronous features but can also be used as limited event emitters or callback aggregators.
+
+More reading:
+
+ - [Promise nuggets](https://promise-nuggets.github.io/)
+ - [Why I am switching to promises](http://spion.github.io/posts/why-i-am-switching-to-promises.html)
+ - [What is the the point of promises](http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/#toc_1)
+ - [Snippets for common problems](https://github.com/petkaantonov/bluebird/wiki/Snippets)
+ - [Promise anti-patterns](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns)
+
+# Questions and issues
+
+If you find a bug in bluebird or have a feature request, file an issue in the [github issue tracker](https://github.com/petkaantonov/bluebird/issues). Anything else, such as questions for help in using the library, should be posted in [StackOverflow](http://stackoverflow.com/questions/tagged/bluebird) under tags `promise` and `bluebird`.
+
+# Error handling
+
+This is a problem every promise library needs to handle in some way. Unhandled rejections/exceptions don't really have a good agreed-on asynchronous correspondence. The problem is that it is impossible to predict the future and know if a rejected promise will eventually be handled.
+
+There are two common pragmatic attempts at solving the problem that promise libraries do.
+
+The more popular one is to have the user explicitly communicate that they are done and any unhandled rejections should be thrown, like so:
+
+```js
+download().then(...).then(...).done();
+```
+
+For handling this problem, in my opinion, this is completely unacceptable and pointless. The user must remember to explicitly call `.done` and that cannot be justified when the problem is forgetting to create an error handler in the first place.
+
+The second approach, which is what bluebird by default takes, is to call a registered handler if a rejection is unhandled by the start of a second turn. The default handler is to write the stack trace to `stderr` or `console.error` in browsers. This is close to what happens with synchronous code - your code doesn't work as expected and you open console and see a stack trace. Nice.
+
+Of course this is not perfect, if your code for some reason needs to swoop in and attach error handler to some promise after the promise has been hanging around a while then you will see annoying messages. In that case you can use the `.done()` method to signal that any hanging exceptions should be thrown.
+
+If you want to override the default handler for these possibly unhandled rejections, you can pass yours like so:
+
+```js
+Promise.onPossiblyUnhandledRejection(function(error){
+ throw error;
+});
+```
+
+If you want to also enable long stack traces, call:
+
+```js
+Promise.longStackTraces();
+```
+
+right after the library is loaded.
+
+In node.js use the environment flag `BLUEBIRD_DEBUG`:
+
+```
+BLUEBIRD_DEBUG=1 node server.js
+```
+
+to enable long stack traces in all instances of bluebird.
+
+Long stack traces cannot be disabled after being enabled, and cannot be enabled after promises have already been created. Long stack traces imply a substantial performance penalty, even after using every trick to optimize them.
+
+Long stack traces are enabled by default in the debug build.
+
+#### Expected and unexpected errors
+
+A practical problem with Promises/A+ is that it models Javascript `try-catch` too closely for its own good. Therefore by default promises inherit `try-catch` warts such as the inability to specify the error types that the catch block is eligible for. It is an anti-pattern in every other language to use catch-all handlers because they swallow exceptions that you might not know about.
+
+Now, Javascript does have a perfectly fine and working way of creating error type hierarchies. It is still quite awkward to use them with the built-in `try-catch` however:
+
+```js
+try {
+ //code
+}
+catch(e) {
+ if( e instanceof WhatIWantError) {
+ //handle
+ }
+ else {
+ throw e;
+ }
+}
+```
+
+Without such checking, unexpected errors would be silently swallowed. However, with promises, bluebird brings the future (hopefully) here now and extends the `.catch` to [accept potential error type eligibility](API.md#catchfunction-errorclass-function-handler---promise).
+
+For instance here it is expected that some evil or incompetent entity will try to crash our server from `SyntaxError` by providing syntactically invalid JSON:
+
+```js
+getJSONFromSomewhere().then(function(jsonString) {
+ return JSON.parse(jsonString);
+}).then(function(object) {
+ console.log("it was valid json: ", object);
+}).catch(SyntaxError, function(e){
+ console.log("don't be evil");
+});
+```
+
+Here any kind of unexpected error will automatically reported on `stderr` along with a stack trace because we only register a handler for the expected `SyntaxError`.
+
+Ok, so, that's pretty neat. But actually not many libraries define error types and it is in fact a complete ghetto out there with ad hoc strings being attached as some arbitrary property name like `.name`, `.type`, `.code`, not having any property at all or even throwing strings as errors and so on. So how can we still listen for expected errors?
+
+Bluebird defines a special error type `OperationalError` (you can get a reference from `Promise.OperationalError`). This type of error is given as rejection reason by promisified methods when
+their underlying library gives an untyped, but expected error. Primitives such as strings, and error objects that are directly created like `new Error("database didn't respond")` are considered untyped.
+
+Example of such library is the node core library `fs`. So if we promisify it, we can catch just the errors we want pretty easily and have programmer errors be redirected to unhandled rejection handler so that we notice them:
+
+```js
+//Read more about promisification in the API Reference:
+//API.md
+var fs = Promise.promisifyAll(require("fs"));
+
+fs.readFileAsync("myfile.json").then(JSON.parse).then(function (json) {
+ console.log("Successful json")
+}).catch(SyntaxError, function (e) {
+ console.error("file contains invalid json");
+}).catch(Promise.OperationalError, function (e) {
+ console.error("unable to read file, because: ", e.message);
+});
+```
+
+The last `catch` handler is only invoked when the `fs` module explicitly used the `err` argument convention of async callbacks to inform of an expected error. The `OperationalError` instance will contain the original error in its `.cause` property but it does have a direct copy of the `.message` and `.stack` too. In this code any unexpected error - be it in our code or the `fs` module - would not be caught by these handlers and therefore not swallowed.
+
+Since a `catch` handler typed to `Promise.OperationalError` is expected to be used very often, it has a neat shorthand:
+
+```js
+.error(function (e) {
+ console.error("unable to read file, because: ", e.message);
+});
+```
+
+See [API documentation for `.error()`](API.md#error-rejectedhandler----promise)
+
+Finally, Bluebird also supports predicate-based filters. If you pass a
+predicate function instead of an error type, the predicate will receive
+the error as an argument. The return result will be used to determine whether
+the error handler should be called.
+
+Predicates should allow for very fine grained control over caught errors:
+pattern matching, error typesets with set operations and many other techniques
+can be implemented on top of them.
+
+Example of using a predicate-based filter:
+
+```js
+var Promise = require("bluebird");
+var request = Promise.promisify(require("request"));
+
+function clientError(e) {
+ return e.code >= 400 && e.code < 500;
+}
+
+request("http://www.google.com").then(function(contents){
+ console.log(contents);
+}).catch(clientError, function(e){
+ //A client error like 400 Bad Request happened
+});
+```
+
+**Danger:** The JavaScript language allows throwing primitive values like strings. Throwing primitives can lead to worse or no stack traces. Primitives [are not exceptions](http://www.devthought.com/2011/12/22/a-string-is-not-an-error/). You should consider always throwing Error objects when handling exceptions.
+
+<hr>
+
+#### How do long stack traces differ from e.g. Q?
+
+Bluebird attempts to have more elaborate traces. Consider:
+
+```js
+Error.stackTraceLimit = 25;
+Q.longStackSupport = true;
+Q().then(function outer() {
+ return Q().then(function inner() {
+ return Q().then(function evenMoreInner() {
+ a.b.c.d();
+ }).catch(function catcher(e){
+ console.error(e.stack);
+ });
+ })
+});
+```
+
+You will see
+
+ ReferenceError: a is not defined
+ at evenMoreInner (<anonymous>:7:13)
+ From previous event:
+ at inner (<anonymous>:6:20)
+
+Compare to:
+
+```js
+Error.stackTraceLimit = 25;
+Promise.longStackTraces();
+Promise.resolve().then(function outer() {
+ return Promise.resolve().then(function inner() {
+ return Promise.resolve().then(function evenMoreInner() {
+ a.b.c.d()
+ }).catch(function catcher(e){
+ console.error(e.stack);
+ });
+ });
+});
+```
+
+ ReferenceError: a is not defined
+ at evenMoreInner (<anonymous>:7:13)
+ From previous event:
+ at inner (<anonymous>:6:36)
+ From previous event:
+ at outer (<anonymous>:5:32)
+ From previous event:
+ at <anonymous>:4:21
+ at Object.InjectedScript._evaluateOn (<anonymous>:572:39)
+ at Object.InjectedScript._evaluateAndWrap (<anonymous>:531:52)
+ at Object.InjectedScript.evaluate (<anonymous>:450:21)
+
+
+A better and more practical example of the differences can be seen in gorgikosev's [debuggability competition](https://github.com/spion/async-compare#debuggability).
+
+<hr>
+
+# Development
+
+For development tasks such as running benchmarks or testing, you need to clone the repository and install dev-dependencies.
+
+Install [node](http://nodejs.org/) and [npm](https://npmjs.org/)
+
+ git clone git@github.com:petkaantonov/bluebird.git
+ cd bluebird
+ npm install
+
+## Testing
+
+To run all tests, run
+
+ node tools/test
+
+If you need to run generator tests run the `tool/test.js` script with `--harmony` argument and node 0.11+:
+
+ node-dev --harmony tools/test
+
+You may specify an individual test file to run with the `--run` script flag:
+
+ node tools/test --run=cancel.js
+
+
+This enables output from the test and may give a better idea where the test is failing. The paramter to `--run` can be any file name located in `test/mocha` folder.
+
+#### Testing in browsers
+
+To run the test in a browser instead of node, pass the flag `--browser` to the test tool
+
+ node tools/test --run=cancel.js --browser
+
+This will automatically create a server (default port 9999) and open it in your default browser once the tests have been compiled.
+
+Keep the test tab active because some tests are timing-sensitive and will fail if the browser is throttling timeouts. Chrome will do this for example when the tab is not active.
+
+#### Supported options by the test tool
+
+The value of boolean flags is determined by presence, if you want to pass false value for a boolean flag, use the `no-`-prefix e.g. `--no-browser`.
+
+ - `--run=String`. Which tests to run (or compile when testing in browser). Default `"all"`. Can also be a glob string (relative to ./test/mocha folder)
+ - `--cover=String`. Create code coverage using the String as istanbul reporter. Coverage is created in the ./coverage folder. No coverage is created by default, default reporter is `"html"` (use `--cover` to use default reporter).
+ - `--browser` - Whether to compile tests for browsers. Default `false`.
+ - `--port=Number` - Whe port where local server is hosted when testing in browser. Default `9999`
+ - `--execute-browser-tests` - Whether to execute the compiled tests for browser when using `--browser`. Default `true`.
+ - `--open-browser` - Whether to open the default browser when executing browser tests. Default `true`.
+ - `--fake-timers` - Whether to use fake timers (`setTimeout` etc) when running tests in node. Default `true`.
+ - `--js-hint` - Whether to run JSHint on source files. Default `true`.
+ - `--saucelabs` Wheter to create a tunnel to sauce labs and run tests in their VMs instead of your browser when compiling tests for browser.Default `false`.
+
+## Benchmarks
+
+To run a benchmark, run the given command for a benchmark while on the project root. Requires bash (on windows the mingw32 that comes with git works fine too).
+
+Node 0.11.2+ is required to run the generator examples.
+
+### 1\. DoxBee sequential
+
+Currently the most relevant benchmark is @gorkikosev's benchmark in the article [Analysis of generators and other async patterns in node](http://spion.github.io/posts/analysis-generators-and-other-async-patterns-node.html). The benchmark emulates a situation where n amount of users are making a request in parallel to execute some mixed async/sync action. The benchmark has been modified to include a warm-up phase to minimize any JITing during timed sections.
+
+Command: `bench doxbee`
+
+### 2\. Made-up parallel
+
+This made-up scenario runs 15 shimmed queries in parallel.
+
+Command: `bench parallel`
+
+## Custom builds
+
+Custom builds for browsers are supported through a command-line utility.
+
+
+<table>
+ <caption>The following features can be disabled</caption>
+ <thead>
+ <tr>
+ <th>Feature(s)</th>
+ <th>Command line identifier</th>
+ </tr>
+ </thead>
+ <tbody>
+
+ <tr><td><a href="API.md#any---promise"><code>.any</code></a> and <a href="API.md#promiseanyarraydynamicpromise-values---promise"><code>Promise.any</code></a></td><td><code>any</code></td></tr>
+ <tr><td><a href="API.md#race---promise"><code>.race</code></a> and <a href="API.md#promiseracearraypromise-promises---promise"><code>Promise.race</code></a></td><td><code>race</code></td></tr>
+ <tr><td><a href="API.md#callstring-propertyname--dynamic-arg---promise"><code>.call</code></a> and <a href="API.md#getstring-propertyname---promise"><code>.get</code></a></td><td><code>call_get</code></td></tr>
+ <tr><td><a href="API.md#filterfunction-filterer---promise"><code>.filter</code></a> and <a href="API.md#promisefilterarraydynamicpromise-values-function-filterer---promise"><code>Promise.filter</code></a></td><td><code>filter</code></td></tr>
+ <tr><td><a href="API.md#mapfunction-mapper---promise"><code>.map</code></a> and <a href="API.md#promisemaparraydynamicpromise-values-function-mapper---promise"><code>Promise.map</code></a></td><td><code>map</code></td></tr>
+ <tr><td><a href="API.md#reducefunction-reducer--dynamic-initialvalue---promise"><code>.reduce</code></a> and <a href="API.md#promisereducearraydynamicpromise-values-function-reducer--dynamic-initialvalue---promise"><code>Promise.reduce</code></a></td><td><code>reduce</code></td></tr>
+ <tr><td><a href="API.md#props---promise"><code>.props</code></a> and <a href="API.md#promisepropsobjectpromise-object---promise"><code>Promise.props</code></a></td><td><code>props</code></td></tr>
+ <tr><td><a href="API.md#settle---promise"><code>.settle</code></a> and <a href="API.md#promisesettlearraydynamicpromise-values---promise"><code>Promise.settle</code></a></td><td><code>settle</code></td></tr>
+ <tr><td><a href="API.md#someint-count---promise"><code>.some</code></a> and <a href="API.md#promisesomearraydynamicpromise-values-int-count---promise"><code>Promise.some</code></a></td><td><code>some</code></td></tr>
+ <tr><td><a href="API.md#nodeifyfunction-callback---promise"><code>.nodeify</code></a></td><td><code>nodeify</code></td></tr>
+ <tr><td><a href="API.md#promisecoroutinegeneratorfunction-generatorfunction---function"><code>Promise.coroutine</code></a> and <a href="API.md#promisespawngeneratorfunction-generatorfunction---promise"><code>Promise.spawn</code></a></td><td><code>generators</code></td></tr>
+ <tr><td><a href="API.md#progression">Progression</a></td><td><code>progress</code></td></tr>
+ <tr><td><a href="API.md#promisification">Promisification</a></td><td><code>promisify</code></td></tr>
+ <tr><td><a href="API.md#cancellation">Cancellation</a></td><td><code>cancel</code></td></tr>
+ <tr><td><a href="API.md#timers">Timers</a></td><td><code>timers</code></td></tr>
+ <tr><td><a href="API.md#resource-management">Resource management</a></td><td><code>using</code></td></tr>
+
+ </tbody>
+</table>
+
+
+Make sure you have cloned the repo somewhere and did `npm install` successfully.
+
+After that you can run:
+
+ node tools/build --features="core"
+
+
+The above builds the most minimal build you can get. You can add more features separated by spaces from the above list:
+
+ node tools/build --features="core filter map reduce"
+
+The custom build file will be found from `/js/browser/bluebird.js`. It will have a comment that lists the disabled and enabled features.
+
+Note that the build leaves the `/js/main` etc folders with same features so if you use the folder for node.js at the same time, don't forget to build
+a full version afterwards (after having taken a copy of the bluebird.js somewhere):
+
+ node tools/build --debug --main --zalgo --browser --minify
+
+#### Supported options by the build tool
+
+The value of boolean flags is determined by presence, if you want to pass false value for a boolean flag, use the `no-`-prefix e.g. `--no-debug`.
+
+ - `--main` - Whether to build the main build. The main build is placed at `js/main` directory. Default `false`.
+ - `--debug` - Whether to build the debug build. The debug build is placed at `js/debug` directory. Default `false`.
+ - `--zalgo` - Whether to build the zalgo build. The zalgo build is placed at `js/zalgo` directory. Default `false`.
+ - `--browser` - Whether to compile the browser build. The browser build file is placed at `js/browser/bluebird.js` Default `false`.
+ - `--minify` - Whether to minify the compiled browser build. The minified browser build file is placed at `js/browser/bluebird.min.js` Default `true`.
+ - `--features=String` - See [custom builds](#custom-builds)
+
+<hr>
+
+## For library authors
+
+Building a library that depends on bluebird? You should know about a few features.
+
+If your library needs to do something obtrusive like adding or modifying methods on the `Promise` prototype, uses long stack traces or uses a custom unhandled rejection handler then... that's totally ok as long as you don't use `require("bluebird")`. Instead you should create a file
+that creates an isolated copy. For example, creating a file called `bluebird-extended.js` that contains:
+
+```js
+ //NOTE the function call right after
+module.exports = require("bluebird/js/main/promise")();
+```
+
+Your library can then use `var Promise = require("bluebird-extended");` and do whatever it wants with it. Then if the application or other library uses their own bluebird promises they will all play well together because of Promises/A+ thenable assimilation magic.
+
+You should also know about [`.nodeify()`](API.md#nodeifyfunction-callback---promise) which makes it easy to provide a dual callback/promise API.
+
+<hr>
+
+## What is the sync build?
+
+You may now use sync build by:
+
+ var Promise = require("bluebird/zalgo");
+
+The sync build is provided to see how forced asynchronity affects benchmarks. It should not be used in real code due to the implied hazards.
+
+The normal async build gives Promises/A+ guarantees about asynchronous resolution of promises. Some people think this affects performance or just plain love their code having a possibility
+of stack overflow errors and non-deterministic behavior.
+
+The sync build skips the async call trampoline completely, e.g code like:
+
+ async.invoke( this.fn, this, val );
+
+Appears as this in the sync build:
+
+ this.fn(val);
+
+This should pressure the CPU slightly less and thus the sync build should perform better. Indeed it does, but only marginally. The biggest performance boosts are from writing efficient Javascript, not from compromising determinism.
+
+Note that while some benchmarks are waiting for the next event tick, the CPU is actually not in use during that time. So the resulting benchmark result is not completely accurate because on node.js you only care about how much the CPU is taxed. Any time spent on CPU is time the whole process (or server) is paralyzed. And it is not graceful like it would be with threads.
+
+
+```js
+var cache = new Map(); //ES6 Map or DataStructures/Map or whatever...
+function getResult(url) {
+ var resolver = Promise.pending();
+ if (cache.has(url)) {
+ resolver.resolve(cache.get(url));
+ }
+ else {
+ http.get(url, function(err, content) {
+ if (err) resolver.reject(err);
+ else {
+ cache.set(url, content);
+ resolver.resolve(content);
+ }
+ });
+ }
+ return resolver.promise;
+}
+
+
+
+//The result of console.log is truly random without async guarantees
+function guessWhatItPrints( url ) {
+ var i = 3;
+ getResult(url).then(function(){
+ i = 4;
+ });
+ console.log(i);
+}
+```
+
+# Optimization guide
+
+Articles about optimization will be periodically posted in [the wiki section](https://github.com/petkaantonov/bluebird/wiki), polishing edits are welcome.
+
+A single cohesive guide compiled from the articles will probably be done eventually.
+
+# License
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Petka Antonov
+
+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.
--- /dev/null
+## 2.9.25 (2015-04-28)
+
+Bugfixes:
+
+ - Fix crash in node 0.8
+
+## 2.9.24 (2015-04-02)
+
+Bugfixes:
+
+ - Fix not being able to load multiple bluebird copies introduced in 2.9.22 ([#559](.), [#561](.), [#560](.)).
+
+## 2.9.23 (2015-04-02)
+
+Bugfixes:
+
+ - Fix node.js domain propagation ([#521](.)).
+
+## 2.9.22 (2015-04-02)
+
+ - Fix `.promisify` crashing in phantom JS ([#556](.))
+
+## 2.9.21 (2015-03-30)
+
+ - Fix error object's `'stack'`' overwriting causing an error when its defined to be a setter that throws an error ([#552](.)).
+
+## 2.9.20 (2015-03-29)
+
+Bugfixes:
+
+ - Fix regression where there is a long delay between calling `.cancel()` and promise actually getting cancelled in Chrome when long stack traces are enabled
+
+## 2.9.19 (2015-03-29)
+
+Bugfixes:
+
+ - Fix crashing in Chrome when long stack traces are disabled
+
+## 2.9.18 (2015-03-29)
+
+Bugfixes:
+
+ - Fix settlePromises using trampoline
+
+## 2.9.17 (2015-03-29)
+
+
+Bugfixes:
+
+ - Fix Chrome DevTools async stack traceability ([#542](.)).
+
+## 2.9.16 (2015-03-28)
+
+Features:
+
+ - Use setImmediate if available
+
+## 2.9.15 (2015-03-26)
+
+Features:
+
+ - Added `.asCallback` alias for `.nodeify`.
+
+Bugfixes:
+
+ - Don't always use nextTick, but try to pick up setImmediate or setTimeout in NW. Fixes [#534](.), [#525](.)
+ - Make progress a core feature. Fixes [#535](.) Note that progress has been removed in 3.x - this is only a fix necessary for 2.x custom builds.
+
+## 2.9.14 (2015-03-12)
+
+Bugfixes:
+
+ - Always use process.nextTick. Fixes [#525](.)
+
+## 2.9.13 (2015-02-27)
+
+Bugfixes:
+
+ - Fix .each, .filter, .reduce and .map callbacks being called synchornously if the input is immediate. ([#513](.))
+
+## 2.9.12 (2015-02-19)
+
+Bugfixes:
+
+ - Fix memory leak introduced in 2.9.0 ([#502](.))
+
+## 2.9.11 (2015-02-19)
+
+Bugfixes:
+
+ - Fix [#503](.)
+
+## 2.9.10 (2015-02-18)
+
+Bugfixes:
+
+ - Fix [#501](.)
+
+## 2.9.9 (2015-02-12)
+
+Bugfixes:
+
+ - Fix `TypeError: Cannot assign to read only property 'length'` when jsdom has declared a read-only length for all objects to inherit.
+
+## 2.9.8 (2015-02-10)
+
+Bugfixes:
+
+ - Fix regression introduced in 2.9.7 where promisify didn't properly dynamically look up methods on `this`
+
+## 2.9.7 (2015-02-08)
+
+Bugfixes:
+
+ - Fix `promisify` not retaining custom properties of the function. This enables promisifying the `"request"` module's export function and its methods at the same time.
+ - Fix `promisifyAll` methods being dependent on `this` when they are not originally dependent on `this`. This enables e.g. passing promisified `fs` functions directly as callbacks without having to bind them to `fs`.
+ - Fix `process.nextTick` being used over `setImmediate` in node.
+
+## 2.9.6 (2015-02-02)
+
+Bugfixes:
+
+ - Node environment detection can no longer be fooled
+
+## 2.9.5 (2015-02-02)
+
+Misc:
+
+ - Warn when [`.then()`](.) is passed non-functions
+
+## 2.9.4 (2015-01-30)
+
+Bugfixes:
+
+ - Fix [.timeout()](.) not calling `clearTimeout` with the proper handle in node causing the process to wait for unneeded timeout. This was a regression introduced in 2.9.1.
+
+## 2.9.3 (2015-01-27)
+
+Bugfixes:
+
+ - Fix node-webkit compatibility issue ([#467](https://github.com/petkaantonov/bluebird/pull/467))
+ - Fix long stack trace support in recent firefox versions
+
+## 2.9.2 (2015-01-26)
+
+Bugfixes:
+
+ - Fix critical bug regarding to using promisifyAll in browser that was introduced in 2.9.0 ([#466](https://github.com/petkaantonov/bluebird/issues/466)).
+
+Misc:
+
+ - Add `"browser"` entry point to package.json
+
+## 2.9.1 (2015-01-24)
+
+Features:
+
+ - If a bound promise is returned by the callback to [`Promise.method`](#promisemethodfunction-fn---function) and [`Promise.try`](#promisetryfunction-fn--arraydynamicdynamic-arguments--dynamic-ctx----promise), the returned promise will be bound to the same value
+
+## 2.9.0 (2015-01-24)
+
+Features:
+
+ - Add [`Promise.fromNode`](API.md#promisefromnodefunction-resolver---promise)
+ - Add new paramter `value` for [`Promise.bind`](API.md#promisebinddynamic-thisarg--dynamic-value---promise)
+
+Bugfixes:
+
+ - Fix several issues with [`cancellation`](API.md#cancellation) and [`.bind()`](API.md#binddynamic-thisarg---promise) interoperation when `thisArg` is a promise or thenable
+ - Fix promises created in [`disposers`](API#disposerfunction-disposer---disposer) not having proper long stack trace context
+ - Fix [`Promise.join`](API.md#promisejoinpromisethenablevalue-promises-function-handler---promise) sometimes passing the passed in callback function as the last argument to itself.
+
+Misc:
+
+ - Reduce minified full browser build file size by not including unused code generation functionality.
+ - Major internal refactoring related to testing code and source code file layout
+
+## 2.8.2 (2015-01-20)
+
+Features:
+
+ - [Global rejection events](https://github.com/petkaantonov/bluebird/blob/master/API.md#global-rejection-events) are now fired both as DOM3 events and as legacy events in browsers
+
+## 2.8.1 (2015-01-20)
+
+Bugfixes:
+
+ - Fix long stack trace stiching consistency when rejected from thenables
+
+## 2.8.0 (2015-01-19)
+
+Features:
+
+ - Major debuggability improvements:
+ - Long stack traces have been re-designed. They are now much more readable,
+ succint, relevant and consistent across bluebird features.
+ - Long stack traces are supported now in IE10+
+
+## 2.7.1 (2015-01-15)
+
+Bugfixes:
+
+ - Fix [#447](https://github.com/petkaantonov/bluebird/issues/447)
+
+## 2.7.0 (2015-01-15)
+
+Features:
+
+ - Added more context to stack traces originating from coroutines ([#421](https://github.com/petkaantonov/bluebird/issues/421))
+ - Implemented [global rejection events](https://github.com/petkaantonov/bluebird/blob/master/API.md#global-rejection-events) ([#428](https://github.com/petkaantonov/bluebird/issues/428), [#357](https://github.com/petkaantonov/bluebird/issues/357))
+ - [Custom promisifiers](https://github.com/petkaantonov/bluebird/blob/master/API.md#option-promisifier) are now passed the default promisifier which can be used to add enhancements on top of normal node promisification
+ - [Promisification filters](https://github.com/petkaantonov/bluebird/blob/master/API.md#option-filter) are now passed `passesDefaultFilter` boolean
+
+Bugfixes:
+
+ - Fix `.noConflict()` call signature ([#446]())
+ - Fix `Promise.method`ified functions being called with `undefined` when they were called with no arguments
+
+## 2.6.4 (2015-01-12)
+
+Bugfixes:
+
+ - `OperationalErrors` thrown by promisified functions retain custom properties, such as `.code` and `.path`.
+
+## 2.6.3 (2015-01-12)
+
+Bugfixes:
+
+ - Fix [#429](https://github.com/petkaantonov/bluebird/issues/429)
+ - Fix [#432](https://github.com/petkaantonov/bluebird/issues/432)
+ - Fix [#433](https://github.com/petkaantonov/bluebird/issues/433)
+
+## 2.6.2 (2015-01-07)
+
+Bugfixes:
+
+ - Fix [#426](https://github.com/petkaantonov/bluebird/issues/426)
+
+## 2.6.1 (2015-01-07)
+
+Bugfixes:
+
+ - Fixed built browser files not being included in the git tag release for bower
+
+## 2.6.0 (2015-01-06)
+
+Features:
+
+ - Significantly improve parallel promise performance and memory usage (+50% faster, -50% less memory)
+
+
+## 2.5.3 (2014-12-30)
+
+## 2.5.2 (2014-12-29)
+
+Bugfixes:
+
+ - Fix bug where already resolved promise gets attached more handlers while calling its handlers resulting in some handlers not being called
+ - Fix bug where then handlers are not called in the same order as they would run if Promises/A+ 2.3.2 was implemented as adoption
+ - Fix bug where using `Object.create(null)` as a rejection reason would crash bluebird
+
+## 2.5.1 (2014-12-29)
+
+Bugfixes:
+
+ - Fix `.finally` throwing null error when it is derived from a promise that is resolved with a promise that is resolved with a promise
+
+## 2.5.0 (2014-12-28)
+
+Features:
+
+ - [`.get`](#API.md#https://github.com/petkaantonov/bluebird/blob/master/API.md#getstring-propertyname---promise) now supports negative indexing.
+
+Bugfixes:
+
+ - Fix bug with `Promise.method` wrapped function returning a promise that never resolves if the function returns a promise that is resolved with another promise
+ - Fix bug with `Promise.delay` never resolving if the value is a promise that is resolved with another promise
+
+## 2.4.3 (2014-12-28)
+
+Bugfixes:
+
+ - Fix memory leak as described in [this Promises/A+ spec issue](https://github.com/promises-aplus/promises-spec/issues/179).
+
+## 2.4.2 (2014-12-21)
+
+Bugfixes:
+
+ - Fix bug where spread rejected handler is ignored in case of rejection
+ - Fix synchronous scheduler passed to `setScheduler` causing infinite loop
+
+## 2.4.1 (2014-12-20)
+
+Features:
+
+ - Error messages now have links to wiki pages for additional information
+ - Promises now clean up all references (to handlers, child promises etc) as soon as possible.
+
+## 2.4.0 (2014-12-18)
+
+Features:
+
+ - Better filtering of bluebird internal calls in long stack traces, especially when using minified file in browsers
+ - Small performance improvements for all collection methods
+ - Promises now delete references to handlers attached to them as soon as possible
+ - Additional stack traces are now output on stderr/`console.warn` for errors that are thrown in the process/window from rejected `.done()` promises. See [#411](https://github.com/petkaantonov/bluebird/issues/411)
+
+## 2.3.11 (2014-10-31)
+
+Bugfixes:
+
+ - Fix [#371](https://github.com/petkaantonov/bluebird/issues/371), [#373](https://github.com/petkaantonov/bluebird/issues/373)
+
+
+## 2.3.10 (2014-10-28)
+
+Features:
+
+ - `Promise.method` no longer wraps primitive errors
+ - `Promise.try` no longer wraps primitive errors
+
+## 2.3.7 (2014-10-25)
+
+Bugfixes:
+
+ - Fix [#359](https://github.com/petkaantonov/bluebird/issues/359), [#362](https://github.com/petkaantonov/bluebird/issues/362) and [#364](https://github.com/petkaantonov/bluebird/issues/364)
+
+## 2.3.6 (2014-10-15)
+
+Features:
+
+ - Implement [`.reflect()`](API.md#reflect---promisepromiseinspection)
+
+## 2.3.5 (2014-10-06)
+
+Bugfixes:
+
+ - Fix issue when promisifying methods whose names contain the string 'args'
+
+## 2.3.4 (2014-09-27)
+
+ - `P` alias was not declared inside WebWorkers
+
+## 2.3.3 (2014-09-27)
+
+Bugfixes:
+
+ - Fix [#318](https://github.com/petkaantonov/bluebird/issues/318), [#314](https://github.com/petkaantonov/bluebird/issues/#314)
+
+## 2.3.2 (2014-08-25)
+
+Bugfixes:
+
+ - `P` alias for `Promise` now exists in global scope when using browser builds without a module loader, fixing an issue with firefox extensions
+
+## 2.3.1 (2014-08-23)
+
+Features:
+
+ - `.using` can now be used with disposers created from different bluebird copy
+
+## 2.3.0 (2014-08-13)
+
+Features:
+
+ - [`.bind()`](API.md#binddynamic-thisarg---promise) and [`Promise.bind()`](API.md#promisebinddynamic-thisarg---promise) now await for the resolution of the `thisArg` if it's a promise or a thenable
+
+Bugfixes:
+
+ - Fix [#276](https://github.com/petkaantonov/bluebird/issues/276)
+
+## 2.2.2 (2014-07-14)
+
+ - Fix [#259](https://github.com/petkaantonov/bluebird/issues/259)
+
+## 2.2.1 (2014-07-07)
+
+ - Fix multiline error messages only showing the first line
+
+## 2.2.0 (2014-07-07)
+
+Bugfixes:
+
+ - `.any` and `.some` now consistently reject with RangeError when input array contains too few promises
+ - Fix iteration bug with `.reduce` when input array contains already fulfilled promises
+
+## 2.1.3 (2014-06-18)
+
+Bugfixes:
+
+ - Fix [#235](https://github.com/petkaantonov/bluebird/issues/235)
+
+## 2.1.2 (2014-06-15)
+
+Bugfixes:
+
+ - Fix [#232](https://github.com/petkaantonov/bluebird/issues/232)
+
+## 2.1.1 (2014-06-11)
+
+## 2.1.0 (2014-06-11)
+
+Features:
+
+ - Add [`promisifier`](API.md#option-promisifier) option to `Promise.promisifyAll()`
+ - Improve performance of `.props()` and collection methods when used with immediate values
+
+
+Bugfixes:
+
+ - Fix a bug where .reduce calls the callback for an already visited item
+ - Fix a bug where stack trace limit is calculated to be too small, which resulted in too short stack traces
+
+<sub>Add undocumented experimental `yieldHandler` option to `Promise.coroutine`</sub>
+
+## 2.0.7 (2014-06-08)
+## 2.0.6 (2014-06-07)
+## 2.0.5 (2014-06-05)
+## 2.0.4 (2014-06-05)
+## 2.0.3 (2014-06-05)
+## 2.0.2 (2014-06-04)
+## 2.0.1 (2014-06-04)
+
+## 2.0.0 (2014-06-04)
+
+#What's new in 2.0
+
+- [Resource management](API.md#resource-management) - never leak resources again
+- [Promisification](API.md#promisification) on steroids - entire modules can now be promisified with one line of code
+- [`.map()`](API.md#mapfunction-mapper--object-options---promise), [`.each()`](API.md#eachfunction-iterator---promise), [`.filter()`](API.md#filterfunction-filterer--object-options---promise), [`.reduce()`](API.md#reducefunction-reducer--dynamic-initialvalue---promise) reimagined from simple sugar to powerful concurrency coordination tools
+- [API Documentation](API.md) has been reorganized and more elaborate examples added
+- Deprecated [progression](#progression-migration) and [deferreds](#deferred-migration)
+- Improved performance and readability
+
+Features:
+
+- Added [`using()`](API.md#promiseusingpromisedisposer-promise-promisedisposer-promise--function-handler---promise) and [`disposer()`](API.md#disposerfunction-disposer---disposer)
+- [`.map()`](API.md#mapfunction-mapper--object-options---promise) now calls the handler as soon as items in the input array become fulfilled
+- Added a concurrency option to [`.map()`](API.md#mapfunction-mapper--object-options---promise)
+- [`.filter()`](API.md#filterfunction-filterer--object-options---promise) now calls the handler as soon as items in the input array become fulfilled
+- Added a concurrency option to [`.filter()`](API.md#filterfunction-filterer--object-options---promise)
+- [`.reduce()`](API.md#reducefunction-reducer--dynamic-initialvalue---promise) now calls the handler as soon as items in the input array become fulfilled, but in-order
+- Added [`.each()`](API.md#eachfunction-iterator---promise)
+- [`Promise.resolve()`](API.md#promiseresolvedynamic-value---promise) behaves like `Promise.cast`. `Promise.cast` deprecated.
+- [Synchronous inspection](API.md#synchronous-inspection): Removed `.inspect()`, added [`.value()`](API.md#value---dynamic) and [`.reason()`](API.md#reason---dynamic)
+- [`Promise.join()`](API.md#promisejoinpromisethenablevalue-promises-function-handler---promise) now takes a function as the last argument
+- Added [`Promise.setScheduler()`](API.md#promisesetschedulerfunction-scheduler---void)
+- [`.cancel()`](API.md#cancelerror-reason---promise) supports a custom cancellation reason
+- [`.timeout()`](API.md#timeoutint-ms--string-message---promise) now cancels the promise instead of rejecting it
+- [`.nodeify()`](API.md#nodeifyfunction-callback--object-options---promise) now supports passing multiple success results when mapping promises to nodebacks
+- Added `suffix` and `filter` options to [`Promise.promisifyAll()`](API.md#promisepromisifyallobject-target--object-options---object)
+
+Breaking changes:
+
+- Sparse array holes are not skipped by collection methods but treated as existing elements with `undefined` value
+- `.map()` and `.filter()` do not call the given mapper or filterer function in any specific order
+- Removed the `.inspect()` method
+- Yielding an array from a coroutine is not supported by default. You can use [`coroutine.addYieldHandler()`](API.md#promisecoroutineaddyieldhandlerfunction-handler---void) to configure the old behavior (or any behavior you want).
+- [`.any()`](API.md#any---promise) and [`.some()`](API.md#someint-count---promise) no longer use an array as the rejection reason. [`AggregateError`](API.md#aggregateerror) is used instead.
+
+
+## 1.2.4 (2014-04-27)
+
+Bugfixes:
+
+ - Fix promisifyAll causing a syntax error when a method name is not a valid identifier
+ - Fix syntax error when es5.js is used in strict mode
+
+## 1.2.3 (2014-04-17)
+
+Bugfixes:
+
+ - Fix [#179](https://github.com/petkaantonov/bluebird/issues/179)
+
+## 1.2.2 (2014-04-09)
+
+Bugfixes:
+
+ - Promisified methods from promisifyAll no longer call the original method when it is overriden
+ - Nodeify doesn't pass second argument to the callback if the promise is fulfilled with `undefined`
+
+## 1.2.1 (2014-03-31)
+
+Bugfixes:
+
+ - Fix [#168](https://github.com/petkaantonov/bluebird/issues/168)
+
+## 1.2.0 (2014-03-29)
+
+Features:
+
+ - New method: [`.value()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#value---dynamic)
+ - New method: [`.reason()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#reason---dynamic)
+ - New method: [`Promise.onUnhandledRejectionHandled()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#promiseonunhandledrejectionhandledfunction-handler---undefined)
+ - `Promise.map()`, `.map()`, `Promise.filter()` and `.filter()` start calling their callbacks as soon as possible while retaining a correct order. See [`8085922f`](https://github.com/petkaantonov/bluebird/commit/8085922fb95a9987fda0cf2337598ab4a98dc315).
+
+Bugfixes:
+
+ - Fix [#165](https://github.com/petkaantonov/bluebird/issues/165)
+ - Fix [#166](https://github.com/petkaantonov/bluebird/issues/166)
+
+## 1.1.1 (2014-03-18)
+
+Bugfixes:
+
+ - [#138](https://github.com/petkaantonov/bluebird/issues/138)
+ - [#144](https://github.com/petkaantonov/bluebird/issues/144)
+ - [#148](https://github.com/petkaantonov/bluebird/issues/148)
+ - [#151](https://github.com/petkaantonov/bluebird/issues/151)
+
+## 1.1.0 (2014-03-08)
+
+Features:
+
+ - Implement [`Promise.prototype.tap()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#tapfunction-handler---promise)
+ - Implement [`Promise.coroutine.addYieldHandler()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisecoroutineaddyieldhandlerfunction-handler---void)
+ - Deprecate `Promise.prototype.spawn`
+
+Bugfixes:
+
+ - Fix already rejected promises being reported as unhandled when handled through collection methods
+ - Fix browserisfy crashing from checking `process.version.indexOf`
+
+## 1.0.8 (2014-03-03)
+
+Bugfixes:
+
+ - Fix active domain being lost across asynchronous boundaries in Node.JS 10.xx
+
+## 1.0.7 (2014-02-25)
+
+Bugfixes:
+
+ - Fix handled errors being reported
+
+## 1.0.6 (2014-02-17)
+
+Bugfixes:
+
+ - Fix bug with unhandled rejections not being reported
+ when using `Promise.try` or `Promise.method` without
+ attaching further handlers
+
+## 1.0.5 (2014-02-15)
+
+Features:
+
+ - Node.js performance: promisified functions try to check amount of passed arguments in most optimal order
+ - Node.js promisified functions will have same `.length` as the original function minus one (for the callback parameter)
+
+## 1.0.4 (2014-02-09)
+
+Features:
+
+ - Possibly unhandled rejection handler will always get a stack trace, even if the rejection or thrown error was not an error
+ - Unhandled rejections are tracked per promise, not per error. So if you create multiple branches from a single ancestor and that ancestor gets rejected, each branch with no error handler with the end will cause a possibly unhandled rejection handler invocation
+
+Bugfixes:
+
+ - Fix unhandled non-writable objects or primitives not reported by possibly unhandled rejection handler
+
+## 1.0.3 (2014-02-05)
+
+Bugfixes:
+
+ - [#93](https://github.com/petkaantonov/bluebird/issues/88)
+
+## 1.0.2 (2014-02-04)
+
+Features:
+
+ - Significantly improve performance of foreign bluebird thenables
+
+Bugfixes:
+
+ - [#88](https://github.com/petkaantonov/bluebird/issues/88)
+
+## 1.0.1 (2014-01-28)
+
+Features:
+
+ - Error objects that have property `.isAsync = true` will now be caught by `.error()`
+
+Bugfixes:
+
+ - Fix TypeError and RangeError shims not working without `new` operator
+
+## 1.0.0 (2014-01-12)
+
+Features:
+
+ - `.filter`, `.map`, and `.reduce` no longer skip sparse array holes. This is a backwards incompatible change.
+ - Like `.map` and `.filter`, `.reduce` now allows returning promises and thenables from the iteration function.
+
+Bugfixes:
+
+ - [#58](https://github.com/petkaantonov/bluebird/issues/58)
+ - [#61](https://github.com/petkaantonov/bluebird/issues/61)
+ - [#64](https://github.com/petkaantonov/bluebird/issues/64)
+ - [#60](https://github.com/petkaantonov/bluebird/issues/60)
+
+## 0.11.6-1 (2013-12-29)
+
+## 0.11.6-0 (2013-12-29)
+
+Features:
+
+ - You may now return promises and thenables from the filterer function used in `Promise.filter` and `Promise.prototype.filter`.
+
+ - `.error()` now catches additional sources of rejections:
+
+ - Rejections originating from `Promise.reject`
+
+ - Rejections originating from thenables using
+ the `reject` callback
+
+ - Rejections originating from promisified callbacks
+ which use the `errback` argument
+
+ - Rejections originating from `new Promise` constructor
+ where the `reject` callback is called explicitly
+
+ - Rejections originating from `PromiseResolver` where
+ `.reject()` method is called explicitly
+
+Bugfixes:
+
+ - Fix `captureStackTrace` being called when it was `null`
+ - Fix `Promise.map` not unwrapping thenables
+
+## 0.11.5-1 (2013-12-15)
+
+## 0.11.5-0 (2013-12-03)
+
+Features:
+
+ - Improve performance of collection methods
+ - Improve performance of promise chains
+
+## 0.11.4-1 (2013-12-02)
+
+## 0.11.4-0 (2013-12-02)
+
+Bugfixes:
+
+ - Fix `Promise.some` behavior with arguments like negative integers, 0...
+ - Fix stack traces of synchronously throwing promisified functions'
+
+## 0.11.3-0 (2013-12-02)
+
+Features:
+
+ - Improve performance of generators
+
+Bugfixes:
+
+ - Fix critical bug with collection methods.
+
+## 0.11.2-0 (2013-12-02)
+
+Features:
+
+ - Improve performance of all collection methods
+
+## 0.11.1-0 (2013-12-02)
+
+Features:
+
+- Improve overall performance.
+- Improve performance of promisified functions.
+- Improve performance of catch filters.
+- Improve performance of .finally.
+
+Bugfixes:
+
+- Fix `.finally()` rejecting if passed non-function. It will now ignore non-functions like `.then`.
+- Fix `.finally()` not converting thenables returned from the handler to promises.
+- `.spread()` now rejects if the ultimate value given to it is not spreadable.
+
+## 0.11.0-0 (2013-12-02)
+
+Features:
+
+ - Improve overall performance when not using `.bind()` or cancellation.
+ - Promises are now not cancellable by default. This is backwards incompatible change - see [`.cancellable()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#cancellable---promise)
+ - [`Promise.delay`](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisedelaydynamic-value-int-ms---promise)
+ - [`.delay()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#delayint-ms---promise)
+ - [`.timeout()`](https://github.com/petkaantonov/bluebird/blob/master/API.md#timeoutint-ms--string-message---promise)
+
+## 0.10.14-0 (2013-12-01)
+
+Bugfixes:
+
+ - Fix race condition when mixing 3rd party asynchrony.
+
+## 0.10.13-1 (2013-11-30)
+
+## 0.10.13-0 (2013-11-30)
+
+Bugfixes:
+
+ - Fix another bug with progression.
+
+## 0.10.12-0 (2013-11-30)
+
+Bugfixes:
+
+ - Fix bug with progression.
+
+## 0.10.11-4 (2013-11-29)
+
+## 0.10.11-2 (2013-11-29)
+
+Bugfixes:
+
+ - Fix `.race()` not propagating bound values.
+
+## 0.10.11-1 (2013-11-29)
+
+Features:
+
+ - Improve performance of `Promise.race`
+
+## 0.10.11-0 (2013-11-29)
+
+Bugfixes:
+
+ - Fixed `Promise.promisifyAll` invoking property accessors. Only data properties with function values are considered.
+
+## 0.10.10-0 (2013-11-28)
+
+Features:
+
+ - Disable long stack traces in browsers by default. Call `Promise.longStackTraces()` to enable them.
+
+## 0.10.9-1 (2013-11-27)
+
+Bugfixes:
+
+ - Fail early when `new Promise` is constructed incorrectly
+
+## 0.10.9-0 (2013-11-27)
+
+Bugfixes:
+
+ - Promise.props now takes a [thenable-for-collection](https://github.com/petkaantonov/bluebird/blob/f41edac61b7c421608ff439bb5a09b7cffeadcf9/test/mocha/props.js#L197-L217)
+ - All promise collection methods now reject when a promise-or-thenable-for-collection turns out not to give a collection
+
+## 0.10.8-0 (2013-11-25)
+
+Features:
+
+ - All static collection methods take thenable-for-collection
+
+## 0.10.7-0 (2013-11-25)
+
+Features:
+
+ - throw TypeError when thenable resolves with itself
+ - Make .race() and Promise.race() forever pending on empty collections
+
+## 0.10.6-0 (2013-11-25)
+
+Bugfixes:
+
+ - Promise.resolve and PromiseResolver.resolve follow thenables too.
+
+## 0.10.5-0 (2013-11-24)
+
+Bugfixes:
+
+ - Fix infinite loop when thenable resolves with itself
+
+## 0.10.4-1 (2013-11-24)
+
+Bugfixes:
+
+ - Fix a file missing from build. (Critical fix)
+
+## 0.10.4-0 (2013-11-24)
+
+Features:
+
+ - Remove dependency of es5-shim and es5-sham when using ES3.
+
+## 0.10.3-0 (2013-11-24)
+
+Features:
+
+ - Improve performance of `Promise.method`
+
+## 0.10.2-1 (2013-11-24)
+
+Features:
+
+ - Rename PromiseResolver#asCallback to PromiseResolver#callback
+
+## 0.10.2-0 (2013-11-24)
+
+Features:
+
+ - Remove memoization of thenables
+
+## 0.10.1-0 (2013-11-21)
+
+Features:
+
+ - Add methods `Promise.resolve()`, `Promise.reject()`, `Promise.defer()` and `.resolve()`.
+
+## 0.10.0-1 (2013-11-17)
+
+## 0.10.0-0 (2013-11-17)
+
+Features:
+
+ - Implement `Promise.method()`
+ - Implement `.return()`
+ - Implement `.throw()`
+
+Bugfixes:
+
+ - Fix promises being able to use themselves as resolution or follower value
+
+## 0.9.11-1 (2013-11-14)
+
+Features:
+
+ - Implicit `Promise.all()` when yielding an array from generators
+
+## 0.9.11-0 (2013-11-13)
+
+Bugfixes:
+
+ - Fix `.spread` not unwrapping thenables
+
+## 0.9.10-2 (2013-11-13)
+
+Features:
+
+ - Improve performance of promisified functions on V8
+
+Bugfixes:
+
+ - Report unhandled rejections even when long stack traces are disabled
+ - Fix `.error()` showing up in stack traces
+
+## 0.9.10-1 (2013-11-05)
+
+Bugfixes:
+
+ - Catch filter method calls showing in stack traces
+
+## 0.9.10-0 (2013-11-05)
+
+Bugfixes:
+
+ - Support primitives in catch filters
+
+## 0.9.9-0 (2013-11-05)
+
+Features:
+
+ - Add `Promise.race()` and `.race()`
+
+## 0.9.8-0 (2013-11-01)
+
+Bugfixes:
+
+ - Fix bug with `Promise.try` not unwrapping returned promises and thenables
+
+## 0.9.7-0 (2013-10-29)
+
+Bugfixes:
+
+ - Fix bug with build files containing duplicated code for promise.js
+
+## 0.9.6-0 (2013-10-28)
+
+Features:
+
+ - Improve output of reporting unhandled non-errors
+ - Implement RejectionError wrapping and `.error()` method
+
+## 0.9.5-0 (2013-10-27)
+
+Features:
+
+ - Allow fresh copies of the library to be made
+
+## 0.9.4-1 (2013-10-27)
+
+## 0.9.4-0 (2013-10-27)
+
+Bugfixes:
+
+ - Rollback non-working multiple fresh copies feature
+
+## 0.9.3-0 (2013-10-27)
+
+Features:
+
+ - Allow fresh copies of the library to be made
+ - Add more components to customized builds
+
+## 0.9.2-1 (2013-10-25)
+
+## 0.9.2-0 (2013-10-25)
+
+Features:
+
+ - Allow custom builds
+
+## 0.9.1-1 (2013-10-22)
+
+Bugfixes:
+
+ - Fix unhandled rethrown exceptions not reported
+
+## 0.9.1-0 (2013-10-22)
+
+Features:
+
+ - Improve performance of `Promise.try`
+ - Extend `Promise.try` to accept arguments and ctx to make it more usable in promisification of synchronous functions.
+
+## 0.9.0-0 (2013-10-18)
+
+Features:
+
+ - Implement `.bind` and `Promise.bind`
+
+Bugfixes:
+
+ - Fix `.some()` when argument is a pending promise that later resolves to an array
+
+## 0.8.5-1 (2013-10-17)
+
+Features:
+
+ - Enable process wide long stack traces through BLUEBIRD_DEBUG environment variable
+
+## 0.8.5-0 (2013-10-16)
+
+Features:
+
+ - Improve performance of all collection methods
+
+Bugfixes:
+
+ - Fix .finally passing the value to handlers
+ - Remove kew from benchmarks due to bugs in the library breaking the benchmark
+ - Fix some bluebird library calls potentially appearing in stack traces
+
+## 0.8.4-1 (2013-10-15)
+
+Bugfixes:
+
+ - Fix .pending() call showing in long stack traces
+
+## 0.8.4-0 (2013-10-15)
+
+Bugfixes:
+
+ - Fix PromiseArray and its sub-classes swallowing possibly unhandled rejections
+
+## 0.8.3-3 (2013-10-14)
+
+Bugfixes:
+
+ - Fix AMD-declaration using named module.
+
+## 0.8.3-2 (2013-10-14)
+
+Features:
+
+ - The mortals that can handle it may now release Zalgo by `require("bluebird/zalgo");`
+
+## 0.8.3-1 (2013-10-14)
+
+Bugfixes:
+
+ - Fix memory leak when using the same promise to attach handlers over and over again
+
+## 0.8.3-0 (2013-10-13)
+
+Features:
+
+ - Add `Promise.props()` and `Promise.prototype.props()`. They work like `.all()` for object properties.
+
+Bugfixes:
+
+ - Fix bug with .some returning garbage when sparse arrays have rejections
+
+## 0.8.2-2 (2013-10-13)
+
+Features:
+
+ - Improve performance of `.reduce()` when `initialValue` can be synchronously cast to a value
+
+## 0.8.2-1 (2013-10-12)
+
+Bugfixes:
+
+ - Fix .npmignore having irrelevant files
+
+## 0.8.2-0 (2013-10-12)
+
+Features:
+
+ - Improve performance of `.some()`
+
+## 0.8.1-0 (2013-10-11)
+
+Bugfixes:
+
+ - Remove uses of dynamic evaluation (`new Function`, `eval` etc) when strictly not necessary. Use feature detection to use static evaluation to avoid errors when dynamic evaluation is prohibited.
+
+## 0.8.0-3 (2013-10-10)
+
+Features:
+
+ - Add `.asCallback` property to `PromiseResolver`s
+
+## 0.8.0-2 (2013-10-10)
+
+## 0.8.0-1 (2013-10-09)
+
+Features:
+
+ - Improve overall performance. Be able to sustain infinite recursion when using promises.
+
+## 0.8.0-0 (2013-10-09)
+
+Bugfixes:
+
+ - Fix stackoverflow error when function calls itself "synchronously" from a promise handler
+
+## 0.7.12-2 (2013-10-09)
+
+Bugfixes:
+
+ - Fix safari 6 not using `MutationObserver` as a scheduler
+ - Fix process exceptions interfering with internal queue flushing
+
+## 0.7.12-1 (2013-10-09)
+
+Bugfixes:
+
+ - Don't try to detect if generators are available to allow shims to be used
+
+## 0.7.12-0 (2013-10-08)
+
+Features:
+
+ - Promisification now consider all functions on the object and its prototype chain
+ - Individual promisifcation uses current `this` if no explicit receiver is given
+ - Give better stack traces when promisified callbacks throw or errback primitives such as strings by wrapping them in an `Error` object.
+
+Bugfixes:
+
+ - Fix runtime APIs throwing synchronous errors
+
+## 0.7.11-0 (2013-10-08)
+
+Features:
+
+ - Deprecate `Promise.promisify(Object target)` in favor of `Promise.promisifyAll(Object target)` to avoid confusion with function objects
+ - Coroutines now throw error when a non-promise is `yielded`
+
+## 0.7.10-1 (2013-10-05)
+
+Features:
+
+ - Make tests pass Internet Explorer 8
+
+## 0.7.10-0 (2013-10-05)
+
+Features:
+
+ - Create browser tests
+
+## 0.7.9-1 (2013-10-03)
+
+Bugfixes:
+
+ - Fix promise cast bug when thenable fulfills using itself as the fulfillment value
+
+## 0.7.9-0 (2013-10-03)
+
+Features:
+
+ - More performance improvements when long stack traces are enabled
+
+## 0.7.8-1 (2013-10-02)
+
+Features:
+
+ - Performance improvements when long stack traces are enabled
+
+## 0.7.8-0 (2013-10-02)
+
+Bugfixes:
+
+ - Fix promisified methods not turning synchronous exceptions into rejections
+
+## 0.7.7-1 (2013-10-02)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.7-0 (2013-10-01)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.6-0 (2013-09-29)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.5-0 (2013-09-28)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.4-1 (2013-09-28)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.4-0 (2013-09-28)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.3-1 (2013-09-28)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.3-0 (2013-09-27)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.2-0 (2013-09-27)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.1-5 (2013-09-26)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.1-4 (2013-09-25)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.1-3 (2013-09-25)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.1-2 (2013-09-24)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.1-1 (2013-09-24)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.1-0 (2013-09-24)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.0-1 (2013-09-23)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.7.0-0 (2013-09-23)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.5-2 (2013-09-20)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.5-1 (2013-09-18)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.5-0 (2013-09-18)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.4-1 (2013-09-18)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.4-0 (2013-09-18)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.3-4 (2013-09-18)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.3-3 (2013-09-18)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.3-2 (2013-09-16)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.3-1 (2013-09-16)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.3-0 (2013-09-15)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.2-1 (2013-09-14)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.2-0 (2013-09-14)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.1-0 (2013-09-14)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.6.0-0 (2013-09-13)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-6 (2013-09-12)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-5 (2013-09-12)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-4 (2013-09-12)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-3 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-2 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-1 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.9-0 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.8-1 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.8-0 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.7-0 (2013-09-11)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.6-1 (2013-09-10)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.6-0 (2013-09-10)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.5-1 (2013-09-10)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.5-0 (2013-09-09)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.4-1 (2013-09-08)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.4-0 (2013-09-08)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.3-0 (2013-09-07)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.2-0 (2013-09-07)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.1-0 (2013-09-07)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.5.0-0 (2013-09-07)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.4.0-0 (2013-09-06)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.3.0-1 (2013-09-06)
+
+Features:
+
+ - feature
+
+Bugfixes:
+
+ - bugfix
+
+## 0.3.0 (2013-09-06)
--- /dev/null
+/* @preserve
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Petka Antonov
+ *
+ * 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:</p>
+ *
+ * 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.
+ *
+ */
+/**
+ * bluebird build version 2.9.25
+ * Features enabled: core, race, call_get, generators, map, nodeify, promisify, props, reduce, settle, some, cancel, using, filter, any, each, timers
+*/
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Promise=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise) {
+var SomePromiseArray = Promise._SomePromiseArray;
+function any(promises) {
+ var ret = new SomePromiseArray(promises);
+ var promise = ret.promise();
+ ret.setHowMany(1);
+ ret.setUnwrap();
+ ret.init();
+ return promise;
+}
+
+Promise.any = function (promises) {
+ return any(promises);
+};
+
+Promise.prototype.any = function () {
+ return any(this);
+};
+
+};
+
+},{}],2:[function(_dereq_,module,exports){
+"use strict";
+var firstLineError;
+try {throw new Error(); } catch (e) {firstLineError = e;}
+var schedule = _dereq_("./schedule.js");
+var Queue = _dereq_("./queue.js");
+var util = _dereq_("./util.js");
+
+function Async() {
+ this._isTickUsed = false;
+ this._lateQueue = new Queue(16);
+ this._normalQueue = new Queue(16);
+ this._trampolineEnabled = true;
+ var self = this;
+ this.drainQueues = function () {
+ self._drainQueues();
+ };
+ this._schedule =
+ schedule.isStatic ? schedule(this.drainQueues) : schedule;
+}
+
+Async.prototype.disableTrampolineIfNecessary = function() {
+ if (util.hasDevTools) {
+ this._trampolineEnabled = false;
+ }
+};
+
+Async.prototype.enableTrampoline = function() {
+ if (!this._trampolineEnabled) {
+ this._trampolineEnabled = true;
+ this._schedule = function(fn) {
+ setTimeout(fn, 0);
+ };
+ }
+};
+
+Async.prototype.haveItemsQueued = function () {
+ return this._normalQueue.length() > 0;
+};
+
+Async.prototype.throwLater = function(fn, arg) {
+ if (arguments.length === 1) {
+ arg = fn;
+ fn = function () { throw arg; };
+ }
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ if (typeof setTimeout !== "undefined") {
+ setTimeout(function() {
+ fn(arg);
+ }, 0);
+ } else try {
+ this._schedule(function() {
+ fn(arg);
+ });
+ } catch (e) {
+ throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/m3OTXk\u000a");
+ }
+};
+
+Async.prototype._getDomain = function() {};
+
+if (!true) {
+if (util.isNode) {
+ var EventsModule = _dereq_("events");
+
+ var domainGetter = function() {
+ var domain = process.domain;
+ if (domain === null) return undefined;
+ return domain;
+ };
+
+ if (EventsModule.usingDomains) {
+ Async.prototype._getDomain = domainGetter;
+ } else {
+ var descriptor =
+ Object.getOwnPropertyDescriptor(EventsModule, "usingDomains");
+
+ if (descriptor) {
+ if (!descriptor.configurable) {
+ process.on("domainsActivated", function() {
+ Async.prototype._getDomain = domainGetter;
+ });
+ } else {
+ var usingDomains = false;
+ Object.defineProperty(EventsModule, "usingDomains", {
+ configurable: false,
+ enumerable: true,
+ get: function() {
+ return usingDomains;
+ },
+ set: function(value) {
+ if (usingDomains || !value) return;
+ usingDomains = true;
+ Async.prototype._getDomain = domainGetter;
+ util.toFastProperties(process);
+ process.emit("domainsActivated");
+ }
+ });
+ }
+ }
+ }
+}
+}
+
+function AsyncInvokeLater(fn, receiver, arg) {
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ this._lateQueue.push(fn, receiver, arg);
+ this._queueTick();
+}
+
+function AsyncInvoke(fn, receiver, arg) {
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ this._normalQueue.push(fn, receiver, arg);
+ this._queueTick();
+}
+
+function AsyncSettlePromises(promise) {
+ var domain = this._getDomain();
+ if (domain !== undefined) {
+ var fn = domain.bind(promise._settlePromises);
+ this._normalQueue.push(fn, promise, undefined);
+ } else {
+ this._normalQueue._pushOne(promise);
+ }
+ this._queueTick();
+}
+
+if (!util.hasDevTools) {
+ Async.prototype.invokeLater = AsyncInvokeLater;
+ Async.prototype.invoke = AsyncInvoke;
+ Async.prototype.settlePromises = AsyncSettlePromises;
+} else {
+ Async.prototype.invokeLater = function (fn, receiver, arg) {
+ if (this._trampolineEnabled) {
+ AsyncInvokeLater.call(this, fn, receiver, arg);
+ } else {
+ setTimeout(function() {
+ fn.call(receiver, arg);
+ }, 100);
+ }
+ };
+
+ Async.prototype.invoke = function (fn, receiver, arg) {
+ if (this._trampolineEnabled) {
+ AsyncInvoke.call(this, fn, receiver, arg);
+ } else {
+ setTimeout(function() {
+ fn.call(receiver, arg);
+ }, 0);
+ }
+ };
+
+ Async.prototype.settlePromises = function(promise) {
+ if (this._trampolineEnabled) {
+ AsyncSettlePromises.call(this, promise);
+ } else {
+ setTimeout(function() {
+ promise._settlePromises();
+ }, 0);
+ }
+ };
+}
+
+Async.prototype.invokeFirst = function (fn, receiver, arg) {
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ this._normalQueue.unshift(fn, receiver, arg);
+ this._queueTick();
+};
+
+Async.prototype._drainQueue = function(queue) {
+ while (queue.length() > 0) {
+ var fn = queue.shift();
+ if (typeof fn !== "function") {
+ fn._settlePromises();
+ continue;
+ }
+ var receiver = queue.shift();
+ var arg = queue.shift();
+ fn.call(receiver, arg);
+ }
+};
+
+Async.prototype._drainQueues = function () {
+ this._drainQueue(this._normalQueue);
+ this._reset();
+ this._drainQueue(this._lateQueue);
+};
+
+Async.prototype._queueTick = function () {
+ if (!this._isTickUsed) {
+ this._isTickUsed = true;
+ this._schedule(this.drainQueues);
+ }
+};
+
+Async.prototype._reset = function () {
+ this._isTickUsed = false;
+};
+
+module.exports = new Async();
+module.exports.firstLineError = firstLineError;
+
+},{"./queue.js":28,"./schedule.js":31,"./util.js":38,"events":39}],3:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL, tryConvertToPromise) {
+var rejectThis = function(_, e) {
+ this._reject(e);
+};
+
+var targetRejected = function(e, context) {
+ context.promiseRejectionQueued = true;
+ context.bindingPromise._then(rejectThis, rejectThis, null, this, e);
+};
+
+var bindingResolved = function(thisArg, context) {
+ this._setBoundTo(thisArg);
+ if (this._isPending()) {
+ this._resolveCallback(context.target);
+ }
+};
+
+var bindingRejected = function(e, context) {
+ if (!context.promiseRejectionQueued) this._reject(e);
+};
+
+Promise.prototype.bind = function (thisArg) {
+ var maybePromise = tryConvertToPromise(thisArg);
+ var ret = new Promise(INTERNAL);
+ ret._propagateFrom(this, 1);
+ var target = this._target();
+ if (maybePromise instanceof Promise) {
+ var context = {
+ promiseRejectionQueued: false,
+ promise: ret,
+ target: target,
+ bindingPromise: maybePromise
+ };
+ target._then(INTERNAL, targetRejected, ret._progress, ret, context);
+ maybePromise._then(
+ bindingResolved, bindingRejected, ret._progress, ret, context);
+ } else {
+ ret._setBoundTo(thisArg);
+ ret._resolveCallback(target);
+ }
+ return ret;
+};
+
+Promise.prototype._setBoundTo = function (obj) {
+ if (obj !== undefined) {
+ this._bitField = this._bitField | 131072;
+ this._boundTo = obj;
+ } else {
+ this._bitField = this._bitField & (~131072);
+ }
+};
+
+Promise.prototype._isBound = function () {
+ return (this._bitField & 131072) === 131072;
+};
+
+Promise.bind = function (thisArg, value) {
+ var maybePromise = tryConvertToPromise(thisArg);
+ var ret = new Promise(INTERNAL);
+
+ if (maybePromise instanceof Promise) {
+ maybePromise._then(function(thisArg) {
+ ret._setBoundTo(thisArg);
+ ret._resolveCallback(value);
+ }, ret._reject, ret._progress, ret, null);
+ } else {
+ ret._setBoundTo(thisArg);
+ ret._resolveCallback(value);
+ }
+ return ret;
+};
+};
+
+},{}],4:[function(_dereq_,module,exports){
+"use strict";
+var old;
+if (typeof Promise !== "undefined") old = Promise;
+function noConflict() {
+ try { if (Promise === bluebird) Promise = old; }
+ catch (e) {}
+ return bluebird;
+}
+var bluebird = _dereq_("./promise.js")();
+bluebird.noConflict = noConflict;
+module.exports = bluebird;
+
+},{"./promise.js":23}],5:[function(_dereq_,module,exports){
+"use strict";
+var cr = Object.create;
+if (cr) {
+ var callerCache = cr(null);
+ var getterCache = cr(null);
+ callerCache[" size"] = getterCache[" size"] = 0;
+}
+
+module.exports = function(Promise) {
+var util = _dereq_("./util.js");
+var canEvaluate = util.canEvaluate;
+var isIdentifier = util.isIdentifier;
+
+var getMethodCaller;
+var getGetter;
+if (!true) {
+var makeMethodCaller = function (methodName) {
+ return new Function("ensureMethod", " \n\
+ return function(obj) { \n\
+ 'use strict' \n\
+ var len = this.length; \n\
+ ensureMethod(obj, 'methodName'); \n\
+ switch(len) { \n\
+ case 1: return obj.methodName(this[0]); \n\
+ case 2: return obj.methodName(this[0], this[1]); \n\
+ case 3: return obj.methodName(this[0], this[1], this[2]); \n\
+ case 0: return obj.methodName(); \n\
+ default: \n\
+ return obj.methodName.apply(obj, this); \n\
+ } \n\
+ }; \n\
+ ".replace(/methodName/g, methodName))(ensureMethod);
+};
+
+var makeGetter = function (propertyName) {
+ return new Function("obj", " \n\
+ 'use strict'; \n\
+ return obj.propertyName; \n\
+ ".replace("propertyName", propertyName));
+};
+
+var getCompiled = function(name, compiler, cache) {
+ var ret = cache[name];
+ if (typeof ret !== "function") {
+ if (!isIdentifier(name)) {
+ return null;
+ }
+ ret = compiler(name);
+ cache[name] = ret;
+ cache[" size"]++;
+ if (cache[" size"] > 512) {
+ var keys = Object.keys(cache);
+ for (var i = 0; i < 256; ++i) delete cache[keys[i]];
+ cache[" size"] = keys.length - 256;
+ }
+ }
+ return ret;
+};
+
+getMethodCaller = function(name) {
+ return getCompiled(name, makeMethodCaller, callerCache);
+};
+
+getGetter = function(name) {
+ return getCompiled(name, makeGetter, getterCache);
+};
+}
+
+function ensureMethod(obj, methodName) {
+ var fn;
+ if (obj != null) fn = obj[methodName];
+ if (typeof fn !== "function") {
+ var message = "Object " + util.classString(obj) + " has no method '" +
+ util.toString(methodName) + "'";
+ throw new Promise.TypeError(message);
+ }
+ return fn;
+}
+
+function caller(obj) {
+ var methodName = this.pop();
+ var fn = ensureMethod(obj, methodName);
+ return fn.apply(obj, this);
+}
+Promise.prototype.call = function (methodName) {
+ var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];}
+ if (!true) {
+ if (canEvaluate) {
+ var maybeCaller = getMethodCaller(methodName);
+ if (maybeCaller !== null) {
+ return this._then(
+ maybeCaller, undefined, undefined, args, undefined);
+ }
+ }
+ }
+ args.push(methodName);
+ return this._then(caller, undefined, undefined, args, undefined);
+};
+
+function namedGetter(obj) {
+ return obj[this];
+}
+function indexedGetter(obj) {
+ var index = +this;
+ if (index < 0) index = Math.max(0, index + obj.length);
+ return obj[index];
+}
+Promise.prototype.get = function (propertyName) {
+ var isIndex = (typeof propertyName === "number");
+ var getter;
+ if (!isIndex) {
+ if (canEvaluate) {
+ var maybeGetter = getGetter(propertyName);
+ getter = maybeGetter !== null ? maybeGetter : namedGetter;
+ } else {
+ getter = namedGetter;
+ }
+ } else {
+ getter = indexedGetter;
+ }
+ return this._then(getter, undefined, undefined, propertyName, undefined);
+};
+};
+
+},{"./util.js":38}],6:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise) {
+var errors = _dereq_("./errors.js");
+var async = _dereq_("./async.js");
+var CancellationError = errors.CancellationError;
+
+Promise.prototype._cancel = function (reason) {
+ if (!this.isCancellable()) return this;
+ var parent;
+ var promiseToReject = this;
+ while ((parent = promiseToReject._cancellationParent) !== undefined &&
+ parent.isCancellable()) {
+ promiseToReject = parent;
+ }
+ this._unsetCancellable();
+ promiseToReject._target()._rejectCallback(reason, false, true);
+};
+
+Promise.prototype.cancel = function (reason) {
+ if (!this.isCancellable()) return this;
+ if (reason === undefined) reason = new CancellationError();
+ async.invokeLater(this._cancel, this, reason);
+ return this;
+};
+
+Promise.prototype.cancellable = function () {
+ if (this._cancellable()) return this;
+ async.enableTrampoline();
+ this._setCancellable();
+ this._cancellationParent = undefined;
+ return this;
+};
+
+Promise.prototype.uncancellable = function () {
+ var ret = this.then();
+ ret._unsetCancellable();
+ return ret;
+};
+
+Promise.prototype.fork = function (didFulfill, didReject, didProgress) {
+ var ret = this._then(didFulfill, didReject, didProgress,
+ undefined, undefined);
+
+ ret._setCancellable();
+ ret._cancellationParent = undefined;
+ return ret;
+};
+};
+
+},{"./async.js":2,"./errors.js":13}],7:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function() {
+var async = _dereq_("./async.js");
+var util = _dereq_("./util.js");
+var bluebirdFramePattern =
+ /[\\\/]bluebird[\\\/]js[\\\/](main|debug|zalgo|instrumented)/;
+var stackFramePattern = null;
+var formatStack = null;
+var indentStackFrames = false;
+var warn;
+
+function CapturedTrace(parent) {
+ this._parent = parent;
+ var length = this._length = 1 + (parent === undefined ? 0 : parent._length);
+ captureStackTrace(this, CapturedTrace);
+ if (length > 32) this.uncycle();
+}
+util.inherits(CapturedTrace, Error);
+
+CapturedTrace.prototype.uncycle = function() {
+ var length = this._length;
+ if (length < 2) return;
+ var nodes = [];
+ var stackToIndex = {};
+
+ for (var i = 0, node = this; node !== undefined; ++i) {
+ nodes.push(node);
+ node = node._parent;
+ }
+ length = this._length = i;
+ for (var i = length - 1; i >= 0; --i) {
+ var stack = nodes[i].stack;
+ if (stackToIndex[stack] === undefined) {
+ stackToIndex[stack] = i;
+ }
+ }
+ for (var i = 0; i < length; ++i) {
+ var currentStack = nodes[i].stack;
+ var index = stackToIndex[currentStack];
+ if (index !== undefined && index !== i) {
+ if (index > 0) {
+ nodes[index - 1]._parent = undefined;
+ nodes[index - 1]._length = 1;
+ }
+ nodes[i]._parent = undefined;
+ nodes[i]._length = 1;
+ var cycleEdgeNode = i > 0 ? nodes[i - 1] : this;
+
+ if (index < length - 1) {
+ cycleEdgeNode._parent = nodes[index + 1];
+ cycleEdgeNode._parent.uncycle();
+ cycleEdgeNode._length =
+ cycleEdgeNode._parent._length + 1;
+ } else {
+ cycleEdgeNode._parent = undefined;
+ cycleEdgeNode._length = 1;
+ }
+ var currentChildLength = cycleEdgeNode._length + 1;
+ for (var j = i - 2; j >= 0; --j) {
+ nodes[j]._length = currentChildLength;
+ currentChildLength++;
+ }
+ return;
+ }
+ }
+};
+
+CapturedTrace.prototype.parent = function() {
+ return this._parent;
+};
+
+CapturedTrace.prototype.hasParent = function() {
+ return this._parent !== undefined;
+};
+
+CapturedTrace.prototype.attachExtraTrace = function(error) {
+ if (error.__stackCleaned__) return;
+ this.uncycle();
+ var parsed = CapturedTrace.parseStackAndMessage(error);
+ var message = parsed.message;
+ var stacks = [parsed.stack];
+
+ var trace = this;
+ while (trace !== undefined) {
+ stacks.push(cleanStack(trace.stack.split("\n")));
+ trace = trace._parent;
+ }
+ removeCommonRoots(stacks);
+ removeDuplicateOrEmptyJumps(stacks);
+ util.notEnumerableProp(error, "stack", reconstructStack(message, stacks));
+ util.notEnumerableProp(error, "__stackCleaned__", true);
+};
+
+function reconstructStack(message, stacks) {
+ for (var i = 0; i < stacks.length - 1; ++i) {
+ stacks[i].push("From previous event:");
+ stacks[i] = stacks[i].join("\n");
+ }
+ if (i < stacks.length) {
+ stacks[i] = stacks[i].join("\n");
+ }
+ return message + "\n" + stacks.join("\n");
+}
+
+function removeDuplicateOrEmptyJumps(stacks) {
+ for (var i = 0; i < stacks.length; ++i) {
+ if (stacks[i].length === 0 ||
+ ((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) {
+ stacks.splice(i, 1);
+ i--;
+ }
+ }
+}
+
+function removeCommonRoots(stacks) {
+ var current = stacks[0];
+ for (var i = 1; i < stacks.length; ++i) {
+ var prev = stacks[i];
+ var currentLastIndex = current.length - 1;
+ var currentLastLine = current[currentLastIndex];
+ var commonRootMeetPoint = -1;
+
+ for (var j = prev.length - 1; j >= 0; --j) {
+ if (prev[j] === currentLastLine) {
+ commonRootMeetPoint = j;
+ break;
+ }
+ }
+
+ for (var j = commonRootMeetPoint; j >= 0; --j) {
+ var line = prev[j];
+ if (current[currentLastIndex] === line) {
+ current.pop();
+ currentLastIndex--;
+ } else {
+ break;
+ }
+ }
+ current = prev;
+ }
+}
+
+function cleanStack(stack) {
+ var ret = [];
+ for (var i = 0; i < stack.length; ++i) {
+ var line = stack[i];
+ var isTraceLine = stackFramePattern.test(line) ||
+ " (No stack trace)" === line;
+ var isInternalFrame = isTraceLine && shouldIgnore(line);
+ if (isTraceLine && !isInternalFrame) {
+ if (indentStackFrames && line.charAt(0) !== " ") {
+ line = " " + line;
+ }
+ ret.push(line);
+ }
+ }
+ return ret;
+}
+
+function stackFramesAsArray(error) {
+ var stack = error.stack.replace(/\s+$/g, "").split("\n");
+ for (var i = 0; i < stack.length; ++i) {
+ var line = stack[i];
+ if (" (No stack trace)" === line || stackFramePattern.test(line)) {
+ break;
+ }
+ }
+ if (i > 0) {
+ stack = stack.slice(i);
+ }
+ return stack;
+}
+
+CapturedTrace.parseStackAndMessage = function(error) {
+ var stack = error.stack;
+ var message = error.toString();
+ stack = typeof stack === "string" && stack.length > 0
+ ? stackFramesAsArray(error) : [" (No stack trace)"];
+ return {
+ message: message,
+ stack: cleanStack(stack)
+ };
+};
+
+CapturedTrace.formatAndLogError = function(error, title) {
+ if (typeof console !== "undefined") {
+ var message;
+ if (typeof error === "object" || typeof error === "function") {
+ var stack = error.stack;
+ message = title + formatStack(stack, error);
+ } else {
+ message = title + String(error);
+ }
+ if (typeof warn === "function") {
+ warn(message);
+ } else if (typeof console.log === "function" ||
+ typeof console.log === "object") {
+ console.log(message);
+ }
+ }
+};
+
+CapturedTrace.unhandledRejection = function (reason) {
+ CapturedTrace.formatAndLogError(reason, "^--- With additional stack trace: ");
+};
+
+CapturedTrace.isSupported = function () {
+ return typeof captureStackTrace === "function";
+};
+
+CapturedTrace.fireRejectionEvent =
+function(name, localHandler, reason, promise) {
+ var localEventFired = false;
+ try {
+ if (typeof localHandler === "function") {
+ localEventFired = true;
+ if (name === "rejectionHandled") {
+ localHandler(promise);
+ } else {
+ localHandler(reason, promise);
+ }
+ }
+ } catch (e) {
+ async.throwLater(e);
+ }
+
+ var globalEventFired = false;
+ try {
+ globalEventFired = fireGlobalEvent(name, reason, promise);
+ } catch (e) {
+ globalEventFired = true;
+ async.throwLater(e);
+ }
+
+ var domEventFired = false;
+ if (fireDomEvent) {
+ try {
+ domEventFired = fireDomEvent(name.toLowerCase(), {
+ reason: reason,
+ promise: promise
+ });
+ } catch (e) {
+ domEventFired = true;
+ async.throwLater(e);
+ }
+ }
+
+ if (!globalEventFired && !localEventFired && !domEventFired &&
+ name === "unhandledRejection") {
+ CapturedTrace.formatAndLogError(reason, "Unhandled rejection ");
+ }
+};
+
+function formatNonError(obj) {
+ var str;
+ if (typeof obj === "function") {
+ str = "[function " +
+ (obj.name || "anonymous") +
+ "]";
+ } else {
+ str = obj.toString();
+ var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/;
+ if (ruselessToString.test(str)) {
+ try {
+ var newStr = JSON.stringify(obj);
+ str = newStr;
+ }
+ catch(e) {
+
+ }
+ }
+ if (str.length === 0) {
+ str = "(empty array)";
+ }
+ }
+ return ("(<" + snip(str) + ">, no stack trace)");
+}
+
+function snip(str) {
+ var maxChars = 41;
+ if (str.length < maxChars) {
+ return str;
+ }
+ return str.substr(0, maxChars - 3) + "...";
+}
+
+var shouldIgnore = function() { return false; };
+var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;
+function parseLineInfo(line) {
+ var matches = line.match(parseLineInfoRegex);
+ if (matches) {
+ return {
+ fileName: matches[1],
+ line: parseInt(matches[2], 10)
+ };
+ }
+}
+CapturedTrace.setBounds = function(firstLineError, lastLineError) {
+ if (!CapturedTrace.isSupported()) return;
+ var firstStackLines = firstLineError.stack.split("\n");
+ var lastStackLines = lastLineError.stack.split("\n");
+ var firstIndex = -1;
+ var lastIndex = -1;
+ var firstFileName;
+ var lastFileName;
+ for (var i = 0; i < firstStackLines.length; ++i) {
+ var result = parseLineInfo(firstStackLines[i]);
+ if (result) {
+ firstFileName = result.fileName;
+ firstIndex = result.line;
+ break;
+ }
+ }
+ for (var i = 0; i < lastStackLines.length; ++i) {
+ var result = parseLineInfo(lastStackLines[i]);
+ if (result) {
+ lastFileName = result.fileName;
+ lastIndex = result.line;
+ break;
+ }
+ }
+ if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName ||
+ firstFileName !== lastFileName || firstIndex >= lastIndex) {
+ return;
+ }
+
+ shouldIgnore = function(line) {
+ if (bluebirdFramePattern.test(line)) return true;
+ var info = parseLineInfo(line);
+ if (info) {
+ if (info.fileName === firstFileName &&
+ (firstIndex <= info.line && info.line <= lastIndex)) {
+ return true;
+ }
+ }
+ return false;
+ };
+};
+
+var captureStackTrace = (function stackDetection() {
+ var v8stackFramePattern = /^\s*at\s*/;
+ var v8stackFormatter = function(stack, error) {
+ if (typeof stack === "string") return stack;
+
+ if (error.name !== undefined &&
+ error.message !== undefined) {
+ return error.toString();
+ }
+ return formatNonError(error);
+ };
+
+ if (typeof Error.stackTraceLimit === "number" &&
+ typeof Error.captureStackTrace === "function") {
+ Error.stackTraceLimit = Error.stackTraceLimit + 6;
+ stackFramePattern = v8stackFramePattern;
+ formatStack = v8stackFormatter;
+ var captureStackTrace = Error.captureStackTrace;
+
+ shouldIgnore = function(line) {
+ return bluebirdFramePattern.test(line);
+ };
+ return function(receiver, ignoreUntil) {
+ Error.stackTraceLimit = Error.stackTraceLimit + 6;
+ captureStackTrace(receiver, ignoreUntil);
+ Error.stackTraceLimit = Error.stackTraceLimit - 6;
+ };
+ }
+ var err = new Error();
+
+ if (typeof err.stack === "string" &&
+ err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) {
+ stackFramePattern = /@/;
+ formatStack = v8stackFormatter;
+ indentStackFrames = true;
+ return function captureStackTrace(o) {
+ o.stack = new Error().stack;
+ };
+ }
+
+ var hasStackAfterThrow;
+ try { throw new Error(); }
+ catch(e) {
+ hasStackAfterThrow = ("stack" in e);
+ }
+ if (!("stack" in err) && hasStackAfterThrow) {
+ stackFramePattern = v8stackFramePattern;
+ formatStack = v8stackFormatter;
+ return function captureStackTrace(o) {
+ Error.stackTraceLimit = Error.stackTraceLimit + 6;
+ try { throw new Error(); }
+ catch(e) { o.stack = e.stack; }
+ Error.stackTraceLimit = Error.stackTraceLimit - 6;
+ };
+ }
+
+ formatStack = function(stack, error) {
+ if (typeof stack === "string") return stack;
+
+ if ((typeof error === "object" ||
+ typeof error === "function") &&
+ error.name !== undefined &&
+ error.message !== undefined) {
+ return error.toString();
+ }
+ return formatNonError(error);
+ };
+
+ return null;
+
+})([]);
+
+var fireDomEvent;
+var fireGlobalEvent = (function() {
+ if (util.isNode) {
+ return function(name, reason, promise) {
+ if (name === "rejectionHandled") {
+ return process.emit(name, promise);
+ } else {
+ return process.emit(name, reason, promise);
+ }
+ };
+ } else {
+ var customEventWorks = false;
+ var anyEventWorks = true;
+ try {
+ var ev = new self.CustomEvent("test");
+ customEventWorks = ev instanceof CustomEvent;
+ } catch (e) {}
+ if (!customEventWorks) {
+ try {
+ var event = document.createEvent("CustomEvent");
+ event.initCustomEvent("testingtheevent", false, true, {});
+ self.dispatchEvent(event);
+ } catch (e) {
+ anyEventWorks = false;
+ }
+ }
+ if (anyEventWorks) {
+ fireDomEvent = function(type, detail) {
+ var event;
+ if (customEventWorks) {
+ event = new self.CustomEvent(type, {
+ detail: detail,
+ bubbles: false,
+ cancelable: true
+ });
+ } else if (self.dispatchEvent) {
+ event = document.createEvent("CustomEvent");
+ event.initCustomEvent(type, false, true, detail);
+ }
+
+ return event ? !self.dispatchEvent(event) : false;
+ };
+ }
+
+ var toWindowMethodNameMap = {};
+ toWindowMethodNameMap["unhandledRejection"] = ("on" +
+ "unhandledRejection").toLowerCase();
+ toWindowMethodNameMap["rejectionHandled"] = ("on" +
+ "rejectionHandled").toLowerCase();
+
+ return function(name, reason, promise) {
+ var methodName = toWindowMethodNameMap[name];
+ var method = self[methodName];
+ if (!method) return false;
+ if (name === "rejectionHandled") {
+ method.call(self, promise);
+ } else {
+ method.call(self, reason, promise);
+ }
+ return true;
+ };
+ }
+})();
+
+if (typeof console !== "undefined" && typeof console.warn !== "undefined") {
+ warn = function (message) {
+ console.warn(message);
+ };
+ if (util.isNode && process.stderr.isTTY) {
+ warn = function(message) {
+ process.stderr.write("\u001b[31m" + message + "\u001b[39m\n");
+ };
+ } else if (!util.isNode && typeof (new Error().stack) === "string") {
+ warn = function(message) {
+ console.warn("%c" + message, "color: red");
+ };
+ }
+}
+
+return CapturedTrace;
+};
+
+},{"./async.js":2,"./util.js":38}],8:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(NEXT_FILTER) {
+var util = _dereq_("./util.js");
+var errors = _dereq_("./errors.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var keys = _dereq_("./es5.js").keys;
+var TypeError = errors.TypeError;
+
+function CatchFilter(instances, callback, promise) {
+ this._instances = instances;
+ this._callback = callback;
+ this._promise = promise;
+}
+
+function safePredicate(predicate, e) {
+ var safeObject = {};
+ var retfilter = tryCatch(predicate).call(safeObject, e);
+
+ if (retfilter === errorObj) return retfilter;
+
+ var safeKeys = keys(safeObject);
+ if (safeKeys.length) {
+ errorObj.e = new TypeError("Catch filter must inherit from Error or be a simple predicate function\u000a\u000a See http://goo.gl/o84o68\u000a");
+ return errorObj;
+ }
+ return retfilter;
+}
+
+CatchFilter.prototype.doFilter = function (e) {
+ var cb = this._callback;
+ var promise = this._promise;
+ var boundTo = promise._boundTo;
+ for (var i = 0, len = this._instances.length; i < len; ++i) {
+ var item = this._instances[i];
+ var itemIsErrorType = item === Error ||
+ (item != null && item.prototype instanceof Error);
+
+ if (itemIsErrorType && e instanceof item) {
+ var ret = tryCatch(cb).call(boundTo, e);
+ if (ret === errorObj) {
+ NEXT_FILTER.e = ret.e;
+ return NEXT_FILTER;
+ }
+ return ret;
+ } else if (typeof item === "function" && !itemIsErrorType) {
+ var shouldHandle = safePredicate(item, e);
+ if (shouldHandle === errorObj) {
+ e = errorObj.e;
+ break;
+ } else if (shouldHandle) {
+ var ret = tryCatch(cb).call(boundTo, e);
+ if (ret === errorObj) {
+ NEXT_FILTER.e = ret.e;
+ return NEXT_FILTER;
+ }
+ return ret;
+ }
+ }
+ }
+ NEXT_FILTER.e = e;
+ return NEXT_FILTER;
+};
+
+return CatchFilter;
+};
+
+},{"./errors.js":13,"./es5.js":14,"./util.js":38}],9:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, CapturedTrace, isDebugging) {
+var contextStack = [];
+function Context() {
+ this._trace = new CapturedTrace(peekContext());
+}
+Context.prototype._pushContext = function () {
+ if (!isDebugging()) return;
+ if (this._trace !== undefined) {
+ contextStack.push(this._trace);
+ }
+};
+
+Context.prototype._popContext = function () {
+ if (!isDebugging()) return;
+ if (this._trace !== undefined) {
+ contextStack.pop();
+ }
+};
+
+function createContext() {
+ if (isDebugging()) return new Context();
+}
+
+function peekContext() {
+ var lastIndex = contextStack.length - 1;
+ if (lastIndex >= 0) {
+ return contextStack[lastIndex];
+ }
+ return undefined;
+}
+
+Promise.prototype._peekContext = peekContext;
+Promise.prototype._pushContext = Context.prototype._pushContext;
+Promise.prototype._popContext = Context.prototype._popContext;
+
+return createContext;
+};
+
+},{}],10:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, CapturedTrace) {
+var async = _dereq_("./async.js");
+var Warning = _dereq_("./errors.js").Warning;
+var util = _dereq_("./util.js");
+var canAttachTrace = util.canAttachTrace;
+var unhandledRejectionHandled;
+var possiblyUnhandledRejection;
+var debugging = false || (util.isNode &&
+ (!!process.env["BLUEBIRD_DEBUG"] ||
+ process.env["NODE_ENV"] === "development"));
+
+if (debugging) {
+ async.disableTrampolineIfNecessary();
+}
+
+Promise.prototype._ensurePossibleRejectionHandled = function () {
+ this._setRejectionIsUnhandled();
+ async.invokeLater(this._notifyUnhandledRejection, this, undefined);
+};
+
+Promise.prototype._notifyUnhandledRejectionIsHandled = function () {
+ CapturedTrace.fireRejectionEvent("rejectionHandled",
+ unhandledRejectionHandled, undefined, this);
+};
+
+Promise.prototype._notifyUnhandledRejection = function () {
+ if (this._isRejectionUnhandled()) {
+ var reason = this._getCarriedStackTrace() || this._settledValue;
+ this._setUnhandledRejectionIsNotified();
+ CapturedTrace.fireRejectionEvent("unhandledRejection",
+ possiblyUnhandledRejection, reason, this);
+ }
+};
+
+Promise.prototype._setUnhandledRejectionIsNotified = function () {
+ this._bitField = this._bitField | 524288;
+};
+
+Promise.prototype._unsetUnhandledRejectionIsNotified = function () {
+ this._bitField = this._bitField & (~524288);
+};
+
+Promise.prototype._isUnhandledRejectionNotified = function () {
+ return (this._bitField & 524288) > 0;
+};
+
+Promise.prototype._setRejectionIsUnhandled = function () {
+ this._bitField = this._bitField | 2097152;
+};
+
+Promise.prototype._unsetRejectionIsUnhandled = function () {
+ this._bitField = this._bitField & (~2097152);
+ if (this._isUnhandledRejectionNotified()) {
+ this._unsetUnhandledRejectionIsNotified();
+ this._notifyUnhandledRejectionIsHandled();
+ }
+};
+
+Promise.prototype._isRejectionUnhandled = function () {
+ return (this._bitField & 2097152) > 0;
+};
+
+Promise.prototype._setCarriedStackTrace = function (capturedTrace) {
+ this._bitField = this._bitField | 1048576;
+ this._fulfillmentHandler0 = capturedTrace;
+};
+
+Promise.prototype._isCarryingStackTrace = function () {
+ return (this._bitField & 1048576) > 0;
+};
+
+Promise.prototype._getCarriedStackTrace = function () {
+ return this._isCarryingStackTrace()
+ ? this._fulfillmentHandler0
+ : undefined;
+};
+
+Promise.prototype._captureStackTrace = function () {
+ if (debugging) {
+ this._trace = new CapturedTrace(this._peekContext());
+ }
+ return this;
+};
+
+Promise.prototype._attachExtraTrace = function (error, ignoreSelf) {
+ if (debugging && canAttachTrace(error)) {
+ var trace = this._trace;
+ if (trace !== undefined) {
+ if (ignoreSelf) trace = trace._parent;
+ }
+ if (trace !== undefined) {
+ trace.attachExtraTrace(error);
+ } else if (!error.__stackCleaned__) {
+ var parsed = CapturedTrace.parseStackAndMessage(error);
+ util.notEnumerableProp(error, "stack",
+ parsed.message + "\n" + parsed.stack.join("\n"));
+ util.notEnumerableProp(error, "__stackCleaned__", true);
+ }
+ }
+};
+
+Promise.prototype._warn = function(message) {
+ var warning = new Warning(message);
+ var ctx = this._peekContext();
+ if (ctx) {
+ ctx.attachExtraTrace(warning);
+ } else {
+ var parsed = CapturedTrace.parseStackAndMessage(warning);
+ warning.stack = parsed.message + "\n" + parsed.stack.join("\n");
+ }
+ CapturedTrace.formatAndLogError(warning, "");
+};
+
+Promise.onPossiblyUnhandledRejection = function (fn) {
+ possiblyUnhandledRejection = typeof fn === "function" ? fn : undefined;
+};
+
+Promise.onUnhandledRejectionHandled = function (fn) {
+ unhandledRejectionHandled = typeof fn === "function" ? fn : undefined;
+};
+
+Promise.longStackTraces = function () {
+ if (async.haveItemsQueued() &&
+ debugging === false
+ ) {
+ throw new Error("cannot enable long stack traces after promises have been created\u000a\u000a See http://goo.gl/DT1qyG\u000a");
+ }
+ debugging = CapturedTrace.isSupported();
+ if (debugging) {
+ async.disableTrampolineIfNecessary();
+ }
+};
+
+Promise.hasLongStackTraces = function () {
+ return debugging && CapturedTrace.isSupported();
+};
+
+if (!CapturedTrace.isSupported()) {
+ Promise.longStackTraces = function(){};
+ debugging = false;
+}
+
+return function() {
+ return debugging;
+};
+};
+
+},{"./async.js":2,"./errors.js":13,"./util.js":38}],11:[function(_dereq_,module,exports){
+"use strict";
+var util = _dereq_("./util.js");
+var isPrimitive = util.isPrimitive;
+var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver;
+
+module.exports = function(Promise) {
+var returner = function () {
+ return this;
+};
+var thrower = function () {
+ throw this;
+};
+
+var wrapper = function (value, action) {
+ if (action === 1) {
+ return function () {
+ throw value;
+ };
+ } else if (action === 2) {
+ return function () {
+ return value;
+ };
+ }
+};
+
+
+Promise.prototype["return"] =
+Promise.prototype.thenReturn = function (value) {
+ if (wrapsPrimitiveReceiver && isPrimitive(value)) {
+ return this._then(
+ wrapper(value, 2),
+ undefined,
+ undefined,
+ undefined,
+ undefined
+ );
+ }
+ return this._then(returner, undefined, undefined, value, undefined);
+};
+
+Promise.prototype["throw"] =
+Promise.prototype.thenThrow = function (reason) {
+ if (wrapsPrimitiveReceiver && isPrimitive(reason)) {
+ return this._then(
+ wrapper(reason, 1),
+ undefined,
+ undefined,
+ undefined,
+ undefined
+ );
+ }
+ return this._then(thrower, undefined, undefined, reason, undefined);
+};
+};
+
+},{"./util.js":38}],12:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var PromiseReduce = Promise.reduce;
+
+Promise.prototype.each = function (fn) {
+ return PromiseReduce(this, fn, null, INTERNAL);
+};
+
+Promise.each = function (promises, fn) {
+ return PromiseReduce(promises, fn, null, INTERNAL);
+};
+};
+
+},{}],13:[function(_dereq_,module,exports){
+"use strict";
+var es5 = _dereq_("./es5.js");
+var Objectfreeze = es5.freeze;
+var util = _dereq_("./util.js");
+var inherits = util.inherits;
+var notEnumerableProp = util.notEnumerableProp;
+
+function subError(nameProperty, defaultMessage) {
+ function SubError(message) {
+ if (!(this instanceof SubError)) return new SubError(message);
+ notEnumerableProp(this, "message",
+ typeof message === "string" ? message : defaultMessage);
+ notEnumerableProp(this, "name", nameProperty);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.constructor);
+ } else {
+ Error.call(this);
+ }
+ }
+ inherits(SubError, Error);
+ return SubError;
+}
+
+var _TypeError, _RangeError;
+var Warning = subError("Warning", "warning");
+var CancellationError = subError("CancellationError", "cancellation error");
+var TimeoutError = subError("TimeoutError", "timeout error");
+var AggregateError = subError("AggregateError", "aggregate error");
+try {
+ _TypeError = TypeError;
+ _RangeError = RangeError;
+} catch(e) {
+ _TypeError = subError("TypeError", "type error");
+ _RangeError = subError("RangeError", "range error");
+}
+
+var methods = ("join pop push shift unshift slice filter forEach some " +
+ "every map indexOf lastIndexOf reduce reduceRight sort reverse").split(" ");
+
+for (var i = 0; i < methods.length; ++i) {
+ if (typeof Array.prototype[methods[i]] === "function") {
+ AggregateError.prototype[methods[i]] = Array.prototype[methods[i]];
+ }
+}
+
+es5.defineProperty(AggregateError.prototype, "length", {
+ value: 0,
+ configurable: false,
+ writable: true,
+ enumerable: true
+});
+AggregateError.prototype["isOperational"] = true;
+var level = 0;
+AggregateError.prototype.toString = function() {
+ var indent = Array(level * 4 + 1).join(" ");
+ var ret = "\n" + indent + "AggregateError of:" + "\n";
+ level++;
+ indent = Array(level * 4 + 1).join(" ");
+ for (var i = 0; i < this.length; ++i) {
+ var str = this[i] === this ? "[Circular AggregateError]" : this[i] + "";
+ var lines = str.split("\n");
+ for (var j = 0; j < lines.length; ++j) {
+ lines[j] = indent + lines[j];
+ }
+ str = lines.join("\n");
+ ret += str + "\n";
+ }
+ level--;
+ return ret;
+};
+
+function OperationalError(message) {
+ if (!(this instanceof OperationalError))
+ return new OperationalError(message);
+ notEnumerableProp(this, "name", "OperationalError");
+ notEnumerableProp(this, "message", message);
+ this.cause = message;
+ this["isOperational"] = true;
+
+ if (message instanceof Error) {
+ notEnumerableProp(this, "message", message.message);
+ notEnumerableProp(this, "stack", message.stack);
+ } else if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.constructor);
+ }
+
+}
+inherits(OperationalError, Error);
+
+var errorTypes = Error["__BluebirdErrorTypes__"];
+if (!errorTypes) {
+ errorTypes = Objectfreeze({
+ CancellationError: CancellationError,
+ TimeoutError: TimeoutError,
+ OperationalError: OperationalError,
+ RejectionError: OperationalError,
+ AggregateError: AggregateError
+ });
+ notEnumerableProp(Error, "__BluebirdErrorTypes__", errorTypes);
+}
+
+module.exports = {
+ Error: Error,
+ TypeError: _TypeError,
+ RangeError: _RangeError,
+ CancellationError: errorTypes.CancellationError,
+ OperationalError: errorTypes.OperationalError,
+ TimeoutError: errorTypes.TimeoutError,
+ AggregateError: errorTypes.AggregateError,
+ Warning: Warning
+};
+
+},{"./es5.js":14,"./util.js":38}],14:[function(_dereq_,module,exports){
+var isES5 = (function(){
+ "use strict";
+ return this === undefined;
+})();
+
+if (isES5) {
+ module.exports = {
+ freeze: Object.freeze,
+ defineProperty: Object.defineProperty,
+ getDescriptor: Object.getOwnPropertyDescriptor,
+ keys: Object.keys,
+ names: Object.getOwnPropertyNames,
+ getPrototypeOf: Object.getPrototypeOf,
+ isArray: Array.isArray,
+ isES5: isES5,
+ propertyIsWritable: function(obj, prop) {
+ var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
+ return !!(!descriptor || descriptor.writable || descriptor.set);
+ }
+ };
+} else {
+ var has = {}.hasOwnProperty;
+ var str = {}.toString;
+ var proto = {}.constructor.prototype;
+
+ var ObjectKeys = function (o) {
+ var ret = [];
+ for (var key in o) {
+ if (has.call(o, key)) {
+ ret.push(key);
+ }
+ }
+ return ret;
+ };
+
+ var ObjectGetDescriptor = function(o, key) {
+ return {value: o[key]};
+ };
+
+ var ObjectDefineProperty = function (o, key, desc) {
+ o[key] = desc.value;
+ return o;
+ };
+
+ var ObjectFreeze = function (obj) {
+ return obj;
+ };
+
+ var ObjectGetPrototypeOf = function (obj) {
+ try {
+ return Object(obj).constructor.prototype;
+ }
+ catch (e) {
+ return proto;
+ }
+ };
+
+ var ArrayIsArray = function (obj) {
+ try {
+ return str.call(obj) === "[object Array]";
+ }
+ catch(e) {
+ return false;
+ }
+ };
+
+ module.exports = {
+ isArray: ArrayIsArray,
+ keys: ObjectKeys,
+ names: ObjectKeys,
+ defineProperty: ObjectDefineProperty,
+ getDescriptor: ObjectGetDescriptor,
+ freeze: ObjectFreeze,
+ getPrototypeOf: ObjectGetPrototypeOf,
+ isES5: isES5,
+ propertyIsWritable: function() {
+ return true;
+ }
+ };
+}
+
+},{}],15:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var PromiseMap = Promise.map;
+
+Promise.prototype.filter = function (fn, options) {
+ return PromiseMap(this, fn, options, INTERNAL);
+};
+
+Promise.filter = function (promises, fn, options) {
+ return PromiseMap(promises, fn, options, INTERNAL);
+};
+};
+
+},{}],16:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, NEXT_FILTER, tryConvertToPromise) {
+var util = _dereq_("./util.js");
+var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver;
+var isPrimitive = util.isPrimitive;
+var thrower = util.thrower;
+
+function returnThis() {
+ return this;
+}
+function throwThis() {
+ throw this;
+}
+function return$(r) {
+ return function() {
+ return r;
+ };
+}
+function throw$(r) {
+ return function() {
+ throw r;
+ };
+}
+function promisedFinally(ret, reasonOrValue, isFulfilled) {
+ var then;
+ if (wrapsPrimitiveReceiver && isPrimitive(reasonOrValue)) {
+ then = isFulfilled ? return$(reasonOrValue) : throw$(reasonOrValue);
+ } else {
+ then = isFulfilled ? returnThis : throwThis;
+ }
+ return ret._then(then, thrower, undefined, reasonOrValue, undefined);
+}
+
+function finallyHandler(reasonOrValue) {
+ var promise = this.promise;
+ var handler = this.handler;
+
+ var ret = promise._isBound()
+ ? handler.call(promise._boundTo)
+ : handler();
+
+ if (ret !== undefined) {
+ var maybePromise = tryConvertToPromise(ret, promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ return promisedFinally(maybePromise, reasonOrValue,
+ promise.isFulfilled());
+ }
+ }
+
+ if (promise.isRejected()) {
+ NEXT_FILTER.e = reasonOrValue;
+ return NEXT_FILTER;
+ } else {
+ return reasonOrValue;
+ }
+}
+
+function tapHandler(value) {
+ var promise = this.promise;
+ var handler = this.handler;
+
+ var ret = promise._isBound()
+ ? handler.call(promise._boundTo, value)
+ : handler(value);
+
+ if (ret !== undefined) {
+ var maybePromise = tryConvertToPromise(ret, promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ return promisedFinally(maybePromise, value, true);
+ }
+ }
+ return value;
+}
+
+Promise.prototype._passThroughHandler = function (handler, isFinally) {
+ if (typeof handler !== "function") return this.then();
+
+ var promiseAndHandler = {
+ promise: this,
+ handler: handler
+ };
+
+ return this._then(
+ isFinally ? finallyHandler : tapHandler,
+ isFinally ? finallyHandler : undefined, undefined,
+ promiseAndHandler, undefined);
+};
+
+Promise.prototype.lastly =
+Promise.prototype["finally"] = function (handler) {
+ return this._passThroughHandler(handler, true);
+};
+
+Promise.prototype.tap = function (handler) {
+ return this._passThroughHandler(handler, false);
+};
+};
+
+},{"./util.js":38}],17:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise,
+ apiRejection,
+ INTERNAL,
+ tryConvertToPromise) {
+var errors = _dereq_("./errors.js");
+var TypeError = errors.TypeError;
+var util = _dereq_("./util.js");
+var errorObj = util.errorObj;
+var tryCatch = util.tryCatch;
+var yieldHandlers = [];
+
+function promiseFromYieldHandler(value, yieldHandlers, traceParent) {
+ for (var i = 0; i < yieldHandlers.length; ++i) {
+ traceParent._pushContext();
+ var result = tryCatch(yieldHandlers[i])(value);
+ traceParent._popContext();
+ if (result === errorObj) {
+ traceParent._pushContext();
+ var ret = Promise.reject(errorObj.e);
+ traceParent._popContext();
+ return ret;
+ }
+ var maybePromise = tryConvertToPromise(result, traceParent);
+ if (maybePromise instanceof Promise) return maybePromise;
+ }
+ return null;
+}
+
+function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {
+ var promise = this._promise = new Promise(INTERNAL);
+ promise._captureStackTrace();
+ this._stack = stack;
+ this._generatorFunction = generatorFunction;
+ this._receiver = receiver;
+ this._generator = undefined;
+ this._yieldHandlers = typeof yieldHandler === "function"
+ ? [yieldHandler].concat(yieldHandlers)
+ : yieldHandlers;
+}
+
+PromiseSpawn.prototype.promise = function () {
+ return this._promise;
+};
+
+PromiseSpawn.prototype._run = function () {
+ this._generator = this._generatorFunction.call(this._receiver);
+ this._receiver =
+ this._generatorFunction = undefined;
+ this._next(undefined);
+};
+
+PromiseSpawn.prototype._continue = function (result) {
+ if (result === errorObj) {
+ return this._promise._rejectCallback(result.e, false, true);
+ }
+
+ var value = result.value;
+ if (result.done === true) {
+ this._promise._resolveCallback(value);
+ } else {
+ var maybePromise = tryConvertToPromise(value, this._promise);
+ if (!(maybePromise instanceof Promise)) {
+ maybePromise =
+ promiseFromYieldHandler(maybePromise,
+ this._yieldHandlers,
+ this._promise);
+ if (maybePromise === null) {
+ this._throw(
+ new TypeError(
+ "A value %s was yielded that could not be treated as a promise\u000a\u000a See http://goo.gl/4Y4pDk\u000a\u000a".replace("%s", value) +
+ "From coroutine:\u000a" +
+ this._stack.split("\n").slice(1, -7).join("\n")
+ )
+ );
+ return;
+ }
+ }
+ maybePromise._then(
+ this._next,
+ this._throw,
+ undefined,
+ this,
+ null
+ );
+ }
+};
+
+PromiseSpawn.prototype._throw = function (reason) {
+ this._promise._attachExtraTrace(reason);
+ this._promise._pushContext();
+ var result = tryCatch(this._generator["throw"])
+ .call(this._generator, reason);
+ this._promise._popContext();
+ this._continue(result);
+};
+
+PromiseSpawn.prototype._next = function (value) {
+ this._promise._pushContext();
+ var result = tryCatch(this._generator.next).call(this._generator, value);
+ this._promise._popContext();
+ this._continue(result);
+};
+
+Promise.coroutine = function (generatorFunction, options) {
+ if (typeof generatorFunction !== "function") {
+ throw new TypeError("generatorFunction must be a function\u000a\u000a See http://goo.gl/6Vqhm0\u000a");
+ }
+ var yieldHandler = Object(options).yieldHandler;
+ var PromiseSpawn$ = PromiseSpawn;
+ var stack = new Error().stack;
+ return function () {
+ var generator = generatorFunction.apply(this, arguments);
+ var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
+ stack);
+ spawn._generator = generator;
+ spawn._next(undefined);
+ return spawn.promise();
+ };
+};
+
+Promise.coroutine.addYieldHandler = function(fn) {
+ if (typeof fn !== "function") throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ yieldHandlers.push(fn);
+};
+
+Promise.spawn = function (generatorFunction) {
+ if (typeof generatorFunction !== "function") {
+ return apiRejection("generatorFunction must be a function\u000a\u000a See http://goo.gl/6Vqhm0\u000a");
+ }
+ var spawn = new PromiseSpawn(generatorFunction, this);
+ var ret = spawn.promise();
+ spawn._run(Promise.spawn);
+ return ret;
+};
+};
+
+},{"./errors.js":13,"./util.js":38}],18:[function(_dereq_,module,exports){
+"use strict";
+module.exports =
+function(Promise, PromiseArray, tryConvertToPromise, INTERNAL) {
+var util = _dereq_("./util.js");
+var canEvaluate = util.canEvaluate;
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var reject;
+
+if (!true) {
+if (canEvaluate) {
+ var thenCallback = function(i) {
+ return new Function("value", "holder", " \n\
+ 'use strict'; \n\
+ holder.pIndex = value; \n\
+ holder.checkFulfillment(this); \n\
+ ".replace(/Index/g, i));
+ };
+
+ var caller = function(count) {
+ var values = [];
+ for (var i = 1; i <= count; ++i) values.push("holder.p" + i);
+ return new Function("holder", " \n\
+ 'use strict'; \n\
+ var callback = holder.fn; \n\
+ return callback(values); \n\
+ ".replace(/values/g, values.join(", ")));
+ };
+ var thenCallbacks = [];
+ var callers = [undefined];
+ for (var i = 1; i <= 5; ++i) {
+ thenCallbacks.push(thenCallback(i));
+ callers.push(caller(i));
+ }
+
+ var Holder = function(total, fn) {
+ this.p1 = this.p2 = this.p3 = this.p4 = this.p5 = null;
+ this.fn = fn;
+ this.total = total;
+ this.now = 0;
+ };
+
+ Holder.prototype.callers = callers;
+ Holder.prototype.checkFulfillment = function(promise) {
+ var now = this.now;
+ now++;
+ var total = this.total;
+ if (now >= total) {
+ var handler = this.callers[total];
+ promise._pushContext();
+ var ret = tryCatch(handler)(this);
+ promise._popContext();
+ if (ret === errorObj) {
+ promise._rejectCallback(ret.e, false, true);
+ } else {
+ promise._resolveCallback(ret);
+ }
+ } else {
+ this.now = now;
+ }
+ };
+
+ var reject = function (reason) {
+ this._reject(reason);
+ };
+}
+}
+
+Promise.join = function () {
+ var last = arguments.length - 1;
+ var fn;
+ if (last > 0 && typeof arguments[last] === "function") {
+ fn = arguments[last];
+ if (!true) {
+ if (last < 6 && canEvaluate) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ var holder = new Holder(last, fn);
+ var callbacks = thenCallbacks;
+ for (var i = 0; i < last; ++i) {
+ var maybePromise = tryConvertToPromise(arguments[i], ret);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ maybePromise._then(callbacks[i], reject,
+ undefined, ret, holder);
+ } else if (maybePromise._isFulfilled()) {
+ callbacks[i].call(ret,
+ maybePromise._value(), holder);
+ } else {
+ ret._reject(maybePromise._reason());
+ }
+ } else {
+ callbacks[i].call(ret, maybePromise, holder);
+ }
+ }
+ return ret;
+ }
+ }
+ }
+ var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];}
+ if (fn) args.pop();
+ var ret = new PromiseArray(args).promise();
+ return fn !== undefined ? ret.spread(fn) : ret;
+};
+
+};
+
+},{"./util.js":38}],19:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise,
+ PromiseArray,
+ apiRejection,
+ tryConvertToPromise,
+ INTERNAL) {
+var async = _dereq_("./async.js");
+var util = _dereq_("./util.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var PENDING = {};
+var EMPTY_ARRAY = [];
+
+function MappingPromiseArray(promises, fn, limit, _filter) {
+ this.constructor$(promises);
+ this._promise._captureStackTrace();
+ this._callback = fn;
+ this._preservedValues = _filter === INTERNAL
+ ? new Array(this.length())
+ : null;
+ this._limit = limit;
+ this._inFlight = 0;
+ this._queue = limit >= 1 ? [] : EMPTY_ARRAY;
+ async.invoke(init, this, undefined);
+}
+util.inherits(MappingPromiseArray, PromiseArray);
+function init() {this._init$(undefined, -2);}
+
+MappingPromiseArray.prototype._init = function () {};
+
+MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ var values = this._values;
+ var length = this.length();
+ var preservedValues = this._preservedValues;
+ var limit = this._limit;
+ if (values[index] === PENDING) {
+ values[index] = value;
+ if (limit >= 1) {
+ this._inFlight--;
+ this._drainQueue();
+ if (this._isResolved()) return;
+ }
+ } else {
+ if (limit >= 1 && this._inFlight >= limit) {
+ values[index] = value;
+ this._queue.push(index);
+ return;
+ }
+ if (preservedValues !== null) preservedValues[index] = value;
+
+ var callback = this._callback;
+ var receiver = this._promise._boundTo;
+ this._promise._pushContext();
+ var ret = tryCatch(callback).call(receiver, value, index, length);
+ this._promise._popContext();
+ if (ret === errorObj) return this._reject(ret.e);
+
+ var maybePromise = tryConvertToPromise(ret, this._promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ if (limit >= 1) this._inFlight++;
+ values[index] = PENDING;
+ return maybePromise._proxyPromiseArray(this, index);
+ } else if (maybePromise._isFulfilled()) {
+ ret = maybePromise._value();
+ } else {
+ return this._reject(maybePromise._reason());
+ }
+ }
+ values[index] = ret;
+ }
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= length) {
+ if (preservedValues !== null) {
+ this._filter(values, preservedValues);
+ } else {
+ this._resolve(values);
+ }
+
+ }
+};
+
+MappingPromiseArray.prototype._drainQueue = function () {
+ var queue = this._queue;
+ var limit = this._limit;
+ var values = this._values;
+ while (queue.length > 0 && this._inFlight < limit) {
+ if (this._isResolved()) return;
+ var index = queue.pop();
+ this._promiseFulfilled(values[index], index);
+ }
+};
+
+MappingPromiseArray.prototype._filter = function (booleans, values) {
+ var len = values.length;
+ var ret = new Array(len);
+ var j = 0;
+ for (var i = 0; i < len; ++i) {
+ if (booleans[i]) ret[j++] = values[i];
+ }
+ ret.length = j;
+ this._resolve(ret);
+};
+
+MappingPromiseArray.prototype.preservedValues = function () {
+ return this._preservedValues;
+};
+
+function map(promises, fn, options, _filter) {
+ var limit = typeof options === "object" && options !== null
+ ? options.concurrency
+ : 0;
+ limit = typeof limit === "number" &&
+ isFinite(limit) && limit >= 1 ? limit : 0;
+ return new MappingPromiseArray(promises, fn, limit, _filter);
+}
+
+Promise.prototype.map = function (fn, options) {
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+
+ return map(this, fn, options, null).promise();
+};
+
+Promise.map = function (promises, fn, options, _filter) {
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ return map(promises, fn, options, _filter).promise();
+};
+
+
+};
+
+},{"./async.js":2,"./util.js":38}],20:[function(_dereq_,module,exports){
+"use strict";
+module.exports =
+function(Promise, INTERNAL, tryConvertToPromise, apiRejection) {
+var util = _dereq_("./util.js");
+var tryCatch = util.tryCatch;
+
+Promise.method = function (fn) {
+ if (typeof fn !== "function") {
+ throw new Promise.TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ }
+ return function () {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._pushContext();
+ var value = tryCatch(fn).apply(this, arguments);
+ ret._popContext();
+ ret._resolveFromSyncValue(value);
+ return ret;
+ };
+};
+
+Promise.attempt = Promise["try"] = function (fn, args, ctx) {
+ if (typeof fn !== "function") {
+ return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ }
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._pushContext();
+ var value = util.isArray(args)
+ ? tryCatch(fn).apply(ctx, args)
+ : tryCatch(fn).call(ctx, args);
+ ret._popContext();
+ ret._resolveFromSyncValue(value);
+ return ret;
+};
+
+Promise.prototype._resolveFromSyncValue = function (value) {
+ if (value === util.errorObj) {
+ this._rejectCallback(value.e, false, true);
+ } else {
+ this._resolveCallback(value, true);
+ }
+};
+};
+
+},{"./util.js":38}],21:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise) {
+var util = _dereq_("./util.js");
+var async = _dereq_("./async.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+
+function spreadAdapter(val, nodeback) {
+ var promise = this;
+ if (!util.isArray(val)) return successAdapter.call(promise, val, nodeback);
+ var ret = tryCatch(nodeback).apply(promise._boundTo, [null].concat(val));
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+
+function successAdapter(val, nodeback) {
+ var promise = this;
+ var receiver = promise._boundTo;
+ var ret = val === undefined
+ ? tryCatch(nodeback).call(receiver, null)
+ : tryCatch(nodeback).call(receiver, null, val);
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+function errorAdapter(reason, nodeback) {
+ var promise = this;
+ if (!reason) {
+ var target = promise._target();
+ var newReason = target._getCarriedStackTrace();
+ newReason.cause = reason;
+ reason = newReason;
+ }
+ var ret = tryCatch(nodeback).call(promise._boundTo, reason);
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+
+Promise.prototype.asCallback =
+Promise.prototype.nodeify = function (nodeback, options) {
+ if (typeof nodeback == "function") {
+ var adapter = successAdapter;
+ if (options !== undefined && Object(options).spread) {
+ adapter = spreadAdapter;
+ }
+ this._then(
+ adapter,
+ errorAdapter,
+ undefined,
+ this,
+ nodeback
+ );
+ }
+ return this;
+};
+};
+
+},{"./async.js":2,"./util.js":38}],22:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, PromiseArray) {
+var util = _dereq_("./util.js");
+var async = _dereq_("./async.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+
+Promise.prototype.progressed = function (handler) {
+ return this._then(undefined, undefined, handler, undefined, undefined);
+};
+
+Promise.prototype._progress = function (progressValue) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ this._target()._progressUnchecked(progressValue);
+
+};
+
+Promise.prototype._progressHandlerAt = function (index) {
+ return index === 0
+ ? this._progressHandler0
+ : this[(index << 2) + index - 5 + 2];
+};
+
+Promise.prototype._doProgressWith = function (progression) {
+ var progressValue = progression.value;
+ var handler = progression.handler;
+ var promise = progression.promise;
+ var receiver = progression.receiver;
+
+ var ret = tryCatch(handler).call(receiver, progressValue);
+ if (ret === errorObj) {
+ if (ret.e != null &&
+ ret.e.name !== "StopProgressPropagation") {
+ var trace = util.canAttachTrace(ret.e)
+ ? ret.e : new Error(util.toString(ret.e));
+ promise._attachExtraTrace(trace);
+ promise._progress(ret.e);
+ }
+ } else if (ret instanceof Promise) {
+ ret._then(promise._progress, null, null, promise, undefined);
+ } else {
+ promise._progress(ret);
+ }
+};
+
+
+Promise.prototype._progressUnchecked = function (progressValue) {
+ var len = this._length();
+ var progress = this._progress;
+ for (var i = 0; i < len; i++) {
+ var handler = this._progressHandlerAt(i);
+ var promise = this._promiseAt(i);
+ if (!(promise instanceof Promise)) {
+ var receiver = this._receiverAt(i);
+ if (typeof handler === "function") {
+ handler.call(receiver, progressValue, promise);
+ } else if (receiver instanceof PromiseArray &&
+ !receiver._isResolved()) {
+ receiver._promiseProgressed(progressValue, promise);
+ }
+ continue;
+ }
+
+ if (typeof handler === "function") {
+ async.invoke(this._doProgressWith, this, {
+ handler: handler,
+ promise: promise,
+ receiver: this._receiverAt(i),
+ value: progressValue
+ });
+ } else {
+ async.invoke(progress, promise, progressValue);
+ }
+ }
+};
+};
+
+},{"./async.js":2,"./util.js":38}],23:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function() {
+var makeSelfResolutionError = function () {
+ return new TypeError("circular promise resolution chain\u000a\u000a See http://goo.gl/LhFpo0\u000a");
+};
+var reflect = function() {
+ return new Promise.PromiseInspection(this._target());
+};
+var apiRejection = function(msg) {
+ return Promise.reject(new TypeError(msg));
+};
+var util = _dereq_("./util.js");
+var async = _dereq_("./async.js");
+var errors = _dereq_("./errors.js");
+var TypeError = Promise.TypeError = errors.TypeError;
+Promise.RangeError = errors.RangeError;
+Promise.CancellationError = errors.CancellationError;
+Promise.TimeoutError = errors.TimeoutError;
+Promise.OperationalError = errors.OperationalError;
+Promise.RejectionError = errors.OperationalError;
+Promise.AggregateError = errors.AggregateError;
+var INTERNAL = function(){};
+var APPLY = {};
+var NEXT_FILTER = {e: null};
+var tryConvertToPromise = _dereq_("./thenables.js")(Promise, INTERNAL);
+var PromiseArray =
+ _dereq_("./promise_array.js")(Promise, INTERNAL,
+ tryConvertToPromise, apiRejection);
+var CapturedTrace = _dereq_("./captured_trace.js")();
+var isDebugging = _dereq_("./debuggability.js")(Promise, CapturedTrace);
+ /*jshint unused:false*/
+var createContext =
+ _dereq_("./context.js")(Promise, CapturedTrace, isDebugging);
+var CatchFilter = _dereq_("./catch_filter.js")(NEXT_FILTER);
+var PromiseResolver = _dereq_("./promise_resolver.js");
+var nodebackForPromise = PromiseResolver._nodebackForPromise;
+var errorObj = util.errorObj;
+var tryCatch = util.tryCatch;
+function Promise(resolver) {
+ if (typeof resolver !== "function") {
+ throw new TypeError("the promise constructor requires a resolver function\u000a\u000a See http://goo.gl/EC22Yn\u000a");
+ }
+ if (this.constructor !== Promise) {
+ throw new TypeError("the promise constructor cannot be invoked directly\u000a\u000a See http://goo.gl/KsIlge\u000a");
+ }
+ this._bitField = 0;
+ this._fulfillmentHandler0 = undefined;
+ this._rejectionHandler0 = undefined;
+ this._progressHandler0 = undefined;
+ this._promise0 = undefined;
+ this._receiver0 = undefined;
+ this._settledValue = undefined;
+ if (resolver !== INTERNAL) this._resolveFromResolver(resolver);
+}
+
+Promise.prototype.toString = function () {
+ return "[object Promise]";
+};
+
+Promise.prototype.caught = Promise.prototype["catch"] = function (fn) {
+ var len = arguments.length;
+ if (len > 1) {
+ var catchInstances = new Array(len - 1),
+ j = 0, i;
+ for (i = 0; i < len - 1; ++i) {
+ var item = arguments[i];
+ if (typeof item === "function") {
+ catchInstances[j++] = item;
+ } else {
+ return Promise.reject(
+ new TypeError("Catch filter must inherit from Error or be a simple predicate function\u000a\u000a See http://goo.gl/o84o68\u000a"));
+ }
+ }
+ catchInstances.length = j;
+ fn = arguments[i];
+ var catchFilter = new CatchFilter(catchInstances, fn, this);
+ return this._then(undefined, catchFilter.doFilter, undefined,
+ catchFilter, undefined);
+ }
+ return this._then(undefined, fn, undefined, undefined, undefined);
+};
+
+Promise.prototype.reflect = function () {
+ return this._then(reflect, reflect, undefined, this, undefined);
+};
+
+Promise.prototype.then = function (didFulfill, didReject, didProgress) {
+ if (isDebugging() && arguments.length > 0 &&
+ typeof didFulfill !== "function" &&
+ typeof didReject !== "function") {
+ var msg = ".then() only accepts functions but was passed: " +
+ util.classString(didFulfill);
+ if (arguments.length > 1) {
+ msg += ", " + util.classString(didReject);
+ }
+ this._warn(msg);
+ }
+ return this._then(didFulfill, didReject, didProgress,
+ undefined, undefined);
+};
+
+Promise.prototype.done = function (didFulfill, didReject, didProgress) {
+ var promise = this._then(didFulfill, didReject, didProgress,
+ undefined, undefined);
+ promise._setIsFinal();
+};
+
+Promise.prototype.spread = function (didFulfill, didReject) {
+ return this.all()._then(didFulfill, didReject, undefined, APPLY, undefined);
+};
+
+Promise.prototype.isCancellable = function () {
+ return !this.isResolved() &&
+ this._cancellable();
+};
+
+Promise.prototype.toJSON = function () {
+ var ret = {
+ isFulfilled: false,
+ isRejected: false,
+ fulfillmentValue: undefined,
+ rejectionReason: undefined
+ };
+ if (this.isFulfilled()) {
+ ret.fulfillmentValue = this.value();
+ ret.isFulfilled = true;
+ } else if (this.isRejected()) {
+ ret.rejectionReason = this.reason();
+ ret.isRejected = true;
+ }
+ return ret;
+};
+
+Promise.prototype.all = function () {
+ return new PromiseArray(this).promise();
+};
+
+Promise.prototype.error = function (fn) {
+ return this.caught(util.originatesFromRejection, fn);
+};
+
+Promise.is = function (val) {
+ return val instanceof Promise;
+};
+
+Promise.fromNode = function(fn) {
+ var ret = new Promise(INTERNAL);
+ var result = tryCatch(fn)(nodebackForPromise(ret));
+ if (result === errorObj) {
+ ret._rejectCallback(result.e, true, true);
+ }
+ return ret;
+};
+
+Promise.all = function (promises) {
+ return new PromiseArray(promises).promise();
+};
+
+Promise.defer = Promise.pending = function () {
+ var promise = new Promise(INTERNAL);
+ return new PromiseResolver(promise);
+};
+
+Promise.cast = function (obj) {
+ var ret = tryConvertToPromise(obj);
+ if (!(ret instanceof Promise)) {
+ var val = ret;
+ ret = new Promise(INTERNAL);
+ ret._fulfillUnchecked(val);
+ }
+ return ret;
+};
+
+Promise.resolve = Promise.fulfilled = Promise.cast;
+
+Promise.reject = Promise.rejected = function (reason) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._rejectCallback(reason, true);
+ return ret;
+};
+
+Promise.setScheduler = function(fn) {
+ if (typeof fn !== "function") throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ var prev = async._schedule;
+ async._schedule = fn;
+ return prev;
+};
+
+Promise.prototype._then = function (
+ didFulfill,
+ didReject,
+ didProgress,
+ receiver,
+ internalData
+) {
+ var haveInternalData = internalData !== undefined;
+ var ret = haveInternalData ? internalData : new Promise(INTERNAL);
+
+ if (!haveInternalData) {
+ ret._propagateFrom(this, 4 | 1);
+ ret._captureStackTrace();
+ }
+
+ var target = this._target();
+ if (target !== this) {
+ if (receiver === undefined) receiver = this._boundTo;
+ if (!haveInternalData) ret._setIsMigrated();
+ }
+
+ var callbackIndex =
+ target._addCallbacks(didFulfill, didReject, didProgress, ret, receiver);
+
+ if (target._isResolved() && !target._isSettlePromisesQueued()) {
+ async.invoke(
+ target._settlePromiseAtPostResolution, target, callbackIndex);
+ }
+
+ return ret;
+};
+
+Promise.prototype._settlePromiseAtPostResolution = function (index) {
+ if (this._isRejectionUnhandled()) this._unsetRejectionIsUnhandled();
+ this._settlePromiseAt(index);
+};
+
+Promise.prototype._length = function () {
+ return this._bitField & 131071;
+};
+
+Promise.prototype._isFollowingOrFulfilledOrRejected = function () {
+ return (this._bitField & 939524096) > 0;
+};
+
+Promise.prototype._isFollowing = function () {
+ return (this._bitField & 536870912) === 536870912;
+};
+
+Promise.prototype._setLength = function (len) {
+ this._bitField = (this._bitField & -131072) |
+ (len & 131071);
+};
+
+Promise.prototype._setFulfilled = function () {
+ this._bitField = this._bitField | 268435456;
+};
+
+Promise.prototype._setRejected = function () {
+ this._bitField = this._bitField | 134217728;
+};
+
+Promise.prototype._setFollowing = function () {
+ this._bitField = this._bitField | 536870912;
+};
+
+Promise.prototype._setIsFinal = function () {
+ this._bitField = this._bitField | 33554432;
+};
+
+Promise.prototype._isFinal = function () {
+ return (this._bitField & 33554432) > 0;
+};
+
+Promise.prototype._cancellable = function () {
+ return (this._bitField & 67108864) > 0;
+};
+
+Promise.prototype._setCancellable = function () {
+ this._bitField = this._bitField | 67108864;
+};
+
+Promise.prototype._unsetCancellable = function () {
+ this._bitField = this._bitField & (~67108864);
+};
+
+Promise.prototype._setIsMigrated = function () {
+ this._bitField = this._bitField | 4194304;
+};
+
+Promise.prototype._unsetIsMigrated = function () {
+ this._bitField = this._bitField & (~4194304);
+};
+
+Promise.prototype._isMigrated = function () {
+ return (this._bitField & 4194304) > 0;
+};
+
+Promise.prototype._receiverAt = function (index) {
+ var ret = index === 0
+ ? this._receiver0
+ : this[
+ index * 5 - 5 + 4];
+ if (ret === undefined && this._isBound()) {
+ return this._boundTo;
+ }
+ return ret;
+};
+
+Promise.prototype._promiseAt = function (index) {
+ return index === 0
+ ? this._promise0
+ : this[index * 5 - 5 + 3];
+};
+
+Promise.prototype._fulfillmentHandlerAt = function (index) {
+ return index === 0
+ ? this._fulfillmentHandler0
+ : this[index * 5 - 5 + 0];
+};
+
+Promise.prototype._rejectionHandlerAt = function (index) {
+ return index === 0
+ ? this._rejectionHandler0
+ : this[index * 5 - 5 + 1];
+};
+
+Promise.prototype._migrateCallbacks = function (follower, index) {
+ var fulfill = follower._fulfillmentHandlerAt(index);
+ var reject = follower._rejectionHandlerAt(index);
+ var progress = follower._progressHandlerAt(index);
+ var promise = follower._promiseAt(index);
+ var receiver = follower._receiverAt(index);
+ if (promise instanceof Promise) promise._setIsMigrated();
+ this._addCallbacks(fulfill, reject, progress, promise, receiver);
+};
+
+Promise.prototype._addCallbacks = function (
+ fulfill,
+ reject,
+ progress,
+ promise,
+ receiver
+) {
+ var index = this._length();
+
+ if (index >= 131071 - 5) {
+ index = 0;
+ this._setLength(0);
+ }
+
+ if (index === 0) {
+ this._promise0 = promise;
+ if (receiver !== undefined) this._receiver0 = receiver;
+ if (typeof fulfill === "function" && !this._isCarryingStackTrace())
+ this._fulfillmentHandler0 = fulfill;
+ if (typeof reject === "function") this._rejectionHandler0 = reject;
+ if (typeof progress === "function") this._progressHandler0 = progress;
+ } else {
+ var base = index * 5 - 5;
+ this[base + 3] = promise;
+ this[base + 4] = receiver;
+ if (typeof fulfill === "function")
+ this[base + 0] = fulfill;
+ if (typeof reject === "function")
+ this[base + 1] = reject;
+ if (typeof progress === "function")
+ this[base + 2] = progress;
+ }
+ this._setLength(index + 1);
+ return index;
+};
+
+Promise.prototype._setProxyHandlers = function (receiver, promiseSlotValue) {
+ var index = this._length();
+
+ if (index >= 131071 - 5) {
+ index = 0;
+ this._setLength(0);
+ }
+ if (index === 0) {
+ this._promise0 = promiseSlotValue;
+ this._receiver0 = receiver;
+ } else {
+ var base = index * 5 - 5;
+ this[base + 3] = promiseSlotValue;
+ this[base + 4] = receiver;
+ }
+ this._setLength(index + 1);
+};
+
+Promise.prototype._proxyPromiseArray = function (promiseArray, index) {
+ this._setProxyHandlers(promiseArray, index);
+};
+
+Promise.prototype._resolveCallback = function(value, shouldBind) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ if (value === this)
+ return this._rejectCallback(makeSelfResolutionError(), false, true);
+ var maybePromise = tryConvertToPromise(value, this);
+ if (!(maybePromise instanceof Promise)) return this._fulfill(value);
+
+ var propagationFlags = 1 | (shouldBind ? 4 : 0);
+ this._propagateFrom(maybePromise, propagationFlags);
+ var promise = maybePromise._target();
+ if (promise._isPending()) {
+ var len = this._length();
+ for (var i = 0; i < len; ++i) {
+ promise._migrateCallbacks(this, i);
+ }
+ this._setFollowing();
+ this._setLength(0);
+ this._setFollowee(promise);
+ } else if (promise._isFulfilled()) {
+ this._fulfillUnchecked(promise._value());
+ } else {
+ this._rejectUnchecked(promise._reason(),
+ promise._getCarriedStackTrace());
+ }
+};
+
+Promise.prototype._rejectCallback =
+function(reason, synchronous, shouldNotMarkOriginatingFromRejection) {
+ if (!shouldNotMarkOriginatingFromRejection) {
+ util.markAsOriginatingFromRejection(reason);
+ }
+ var trace = util.ensureErrorObject(reason);
+ var hasStack = trace === reason;
+ this._attachExtraTrace(trace, synchronous ? hasStack : false);
+ this._reject(reason, hasStack ? undefined : trace);
+};
+
+Promise.prototype._resolveFromResolver = function (resolver) {
+ var promise = this;
+ this._captureStackTrace();
+ this._pushContext();
+ var synchronous = true;
+ var r = tryCatch(resolver)(function(value) {
+ if (promise === null) return;
+ promise._resolveCallback(value);
+ promise = null;
+ }, function (reason) {
+ if (promise === null) return;
+ promise._rejectCallback(reason, synchronous);
+ promise = null;
+ });
+ synchronous = false;
+ this._popContext();
+
+ if (r !== undefined && r === errorObj && promise !== null) {
+ promise._rejectCallback(r.e, true, true);
+ promise = null;
+ }
+};
+
+Promise.prototype._settlePromiseFromHandler = function (
+ handler, receiver, value, promise
+) {
+ if (promise._isRejected()) return;
+ promise._pushContext();
+ var x;
+ if (receiver === APPLY && !this._isRejected()) {
+ x = tryCatch(handler).apply(this._boundTo, value);
+ } else {
+ x = tryCatch(handler).call(receiver, value);
+ }
+ promise._popContext();
+
+ if (x === errorObj || x === promise || x === NEXT_FILTER) {
+ var err = x === promise ? makeSelfResolutionError() : x.e;
+ promise._rejectCallback(err, false, true);
+ } else {
+ promise._resolveCallback(x);
+ }
+};
+
+Promise.prototype._target = function() {
+ var ret = this;
+ while (ret._isFollowing()) ret = ret._followee();
+ return ret;
+};
+
+Promise.prototype._followee = function() {
+ return this._rejectionHandler0;
+};
+
+Promise.prototype._setFollowee = function(promise) {
+ this._rejectionHandler0 = promise;
+};
+
+Promise.prototype._cleanValues = function () {
+ if (this._cancellable()) {
+ this._cancellationParent = undefined;
+ }
+};
+
+Promise.prototype._propagateFrom = function (parent, flags) {
+ if ((flags & 1) > 0 && parent._cancellable()) {
+ this._setCancellable();
+ this._cancellationParent = parent;
+ }
+ if ((flags & 4) > 0 && parent._isBound()) {
+ this._setBoundTo(parent._boundTo);
+ }
+};
+
+Promise.prototype._fulfill = function (value) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ this._fulfillUnchecked(value);
+};
+
+Promise.prototype._reject = function (reason, carriedStackTrace) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ this._rejectUnchecked(reason, carriedStackTrace);
+};
+
+Promise.prototype._settlePromiseAt = function (index) {
+ var promise = this._promiseAt(index);
+ var isPromise = promise instanceof Promise;
+
+ if (isPromise && promise._isMigrated()) {
+ promise._unsetIsMigrated();
+ return async.invoke(this._settlePromiseAt, this, index);
+ }
+ var handler = this._isFulfilled()
+ ? this._fulfillmentHandlerAt(index)
+ : this._rejectionHandlerAt(index);
+
+ var carriedStackTrace =
+ this._isCarryingStackTrace() ? this._getCarriedStackTrace() : undefined;
+ var value = this._settledValue;
+ var receiver = this._receiverAt(index);
+
+
+ this._clearCallbackDataAtIndex(index);
+
+ if (typeof handler === "function") {
+ if (!isPromise) {
+ handler.call(receiver, value, promise);
+ } else {
+ this._settlePromiseFromHandler(handler, receiver, value, promise);
+ }
+ } else if (receiver instanceof PromiseArray) {
+ if (!receiver._isResolved()) {
+ if (this._isFulfilled()) {
+ receiver._promiseFulfilled(value, promise);
+ }
+ else {
+ receiver._promiseRejected(value, promise);
+ }
+ }
+ } else if (isPromise) {
+ if (this._isFulfilled()) {
+ promise._fulfill(value);
+ } else {
+ promise._reject(value, carriedStackTrace);
+ }
+ }
+
+ if (index >= 4 && (index & 31) === 4)
+ async.invokeLater(this._setLength, this, 0);
+};
+
+Promise.prototype._clearCallbackDataAtIndex = function(index) {
+ if (index === 0) {
+ if (!this._isCarryingStackTrace()) {
+ this._fulfillmentHandler0 = undefined;
+ }
+ this._rejectionHandler0 =
+ this._progressHandler0 =
+ this._receiver0 =
+ this._promise0 = undefined;
+ } else {
+ var base = index * 5 - 5;
+ this[base + 3] =
+ this[base + 4] =
+ this[base + 0] =
+ this[base + 1] =
+ this[base + 2] = undefined;
+ }
+};
+
+Promise.prototype._isSettlePromisesQueued = function () {
+ return (this._bitField &
+ -1073741824) === -1073741824;
+};
+
+Promise.prototype._setSettlePromisesQueued = function () {
+ this._bitField = this._bitField | -1073741824;
+};
+
+Promise.prototype._unsetSettlePromisesQueued = function () {
+ this._bitField = this._bitField & (~-1073741824);
+};
+
+Promise.prototype._queueSettlePromises = function() {
+ async.settlePromises(this);
+ this._setSettlePromisesQueued();
+};
+
+Promise.prototype._fulfillUnchecked = function (value) {
+ if (value === this) {
+ var err = makeSelfResolutionError();
+ this._attachExtraTrace(err);
+ return this._rejectUnchecked(err, undefined);
+ }
+ this._setFulfilled();
+ this._settledValue = value;
+ this._cleanValues();
+
+ if (this._length() > 0) {
+ this._queueSettlePromises();
+ }
+};
+
+Promise.prototype._rejectUncheckedCheckError = function (reason) {
+ var trace = util.ensureErrorObject(reason);
+ this._rejectUnchecked(reason, trace === reason ? undefined : trace);
+};
+
+Promise.prototype._rejectUnchecked = function (reason, trace) {
+ if (reason === this) {
+ var err = makeSelfResolutionError();
+ this._attachExtraTrace(err);
+ return this._rejectUnchecked(err);
+ }
+ this._setRejected();
+ this._settledValue = reason;
+ this._cleanValues();
+
+ if (this._isFinal()) {
+ async.throwLater(function(e) {
+ if ("stack" in e) {
+ async.invokeFirst(
+ CapturedTrace.unhandledRejection, undefined, e);
+ }
+ throw e;
+ }, trace === undefined ? reason : trace);
+ return;
+ }
+
+ if (trace !== undefined && trace !== reason) {
+ this._setCarriedStackTrace(trace);
+ }
+
+ if (this._length() > 0) {
+ this._queueSettlePromises();
+ } else {
+ this._ensurePossibleRejectionHandled();
+ }
+};
+
+Promise.prototype._settlePromises = function () {
+ this._unsetSettlePromisesQueued();
+ var len = this._length();
+ for (var i = 0; i < len; i++) {
+ this._settlePromiseAt(i);
+ }
+};
+
+Promise._makeSelfResolutionError = makeSelfResolutionError;
+_dereq_("./progress.js")(Promise, PromiseArray);
+_dereq_("./method.js")(Promise, INTERNAL, tryConvertToPromise, apiRejection);
+_dereq_("./bind.js")(Promise, INTERNAL, tryConvertToPromise);
+_dereq_("./finally.js")(Promise, NEXT_FILTER, tryConvertToPromise);
+_dereq_("./direct_resolve.js")(Promise);
+_dereq_("./synchronous_inspection.js")(Promise);
+_dereq_("./join.js")(Promise, PromiseArray, tryConvertToPromise, INTERNAL);
+Promise.Promise = Promise;
+_dereq_('./map.js')(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL);
+_dereq_('./cancel.js')(Promise);
+_dereq_('./using.js')(Promise, apiRejection, tryConvertToPromise, createContext);
+_dereq_('./generators.js')(Promise, apiRejection, INTERNAL, tryConvertToPromise);
+_dereq_('./nodeify.js')(Promise);
+_dereq_('./call_get.js')(Promise);
+_dereq_('./props.js')(Promise, PromiseArray, tryConvertToPromise, apiRejection);
+_dereq_('./race.js')(Promise, INTERNAL, tryConvertToPromise, apiRejection);
+_dereq_('./reduce.js')(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL);
+_dereq_('./settle.js')(Promise, PromiseArray);
+_dereq_('./some.js')(Promise, PromiseArray, apiRejection);
+_dereq_('./promisify.js')(Promise, INTERNAL);
+_dereq_('./any.js')(Promise);
+_dereq_('./each.js')(Promise, INTERNAL);
+_dereq_('./timers.js')(Promise, INTERNAL);
+_dereq_('./filter.js')(Promise, INTERNAL);
+
+ util.toFastProperties(Promise);
+ util.toFastProperties(Promise.prototype);
+ function fillTypes(value) {
+ var p = new Promise(INTERNAL);
+ p._fulfillmentHandler0 = value;
+ p._rejectionHandler0 = value;
+ p._progressHandler0 = value;
+ p._promise0 = value;
+ p._receiver0 = value;
+ p._settledValue = value;
+ }
+ // Complete slack tracking, opt out of field-type tracking and
+ // stabilize map
+ fillTypes({a: 1});
+ fillTypes({b: 2});
+ fillTypes({c: 3});
+ fillTypes(1);
+ fillTypes(function(){});
+ fillTypes(undefined);
+ fillTypes(false);
+ fillTypes(new Promise(INTERNAL));
+ CapturedTrace.setBounds(async.firstLineError, util.lastLineError);
+ return Promise;
+
+};
+
+},{"./any.js":1,"./async.js":2,"./bind.js":3,"./call_get.js":5,"./cancel.js":6,"./captured_trace.js":7,"./catch_filter.js":8,"./context.js":9,"./debuggability.js":10,"./direct_resolve.js":11,"./each.js":12,"./errors.js":13,"./filter.js":15,"./finally.js":16,"./generators.js":17,"./join.js":18,"./map.js":19,"./method.js":20,"./nodeify.js":21,"./progress.js":22,"./promise_array.js":24,"./promise_resolver.js":25,"./promisify.js":26,"./props.js":27,"./race.js":29,"./reduce.js":30,"./settle.js":32,"./some.js":33,"./synchronous_inspection.js":34,"./thenables.js":35,"./timers.js":36,"./using.js":37,"./util.js":38}],24:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL, tryConvertToPromise,
+ apiRejection) {
+var util = _dereq_("./util.js");
+var isArray = util.isArray;
+
+function toResolutionValue(val) {
+ switch(val) {
+ case -2: return [];
+ case -3: return {};
+ }
+}
+
+function PromiseArray(values) {
+ var promise = this._promise = new Promise(INTERNAL);
+ var parent;
+ if (values instanceof Promise) {
+ parent = values;
+ promise._propagateFrom(parent, 1 | 4);
+ }
+ this._values = values;
+ this._length = 0;
+ this._totalResolved = 0;
+ this._init(undefined, -2);
+}
+PromiseArray.prototype.length = function () {
+ return this._length;
+};
+
+PromiseArray.prototype.promise = function () {
+ return this._promise;
+};
+
+PromiseArray.prototype._init = function init(_, resolveValueIfEmpty) {
+ var values = tryConvertToPromise(this._values, this._promise);
+ if (values instanceof Promise) {
+ values = values._target();
+ this._values = values;
+ if (values._isFulfilled()) {
+ values = values._value();
+ if (!isArray(values)) {
+ var err = new Promise.TypeError("expecting an array, a promise or a thenable\u000a\u000a See http://goo.gl/s8MMhc\u000a");
+ this.__hardReject__(err);
+ return;
+ }
+ } else if (values._isPending()) {
+ values._then(
+ init,
+ this._reject,
+ undefined,
+ this,
+ resolveValueIfEmpty
+ );
+ return;
+ } else {
+ this._reject(values._reason());
+ return;
+ }
+ } else if (!isArray(values)) {
+ this._promise._reject(apiRejection("expecting an array, a promise or a thenable\u000a\u000a See http://goo.gl/s8MMhc\u000a")._reason());
+ return;
+ }
+
+ if (values.length === 0) {
+ if (resolveValueIfEmpty === -5) {
+ this._resolveEmptyArray();
+ }
+ else {
+ this._resolve(toResolutionValue(resolveValueIfEmpty));
+ }
+ return;
+ }
+ var len = this.getActualLength(values.length);
+ this._length = len;
+ this._values = this.shouldCopyValues() ? new Array(len) : this._values;
+ var promise = this._promise;
+ for (var i = 0; i < len; ++i) {
+ var isResolved = this._isResolved();
+ var maybePromise = tryConvertToPromise(values[i], promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (isResolved) {
+ maybePromise._unsetRejectionIsUnhandled();
+ } else if (maybePromise._isPending()) {
+ maybePromise._proxyPromiseArray(this, i);
+ } else if (maybePromise._isFulfilled()) {
+ this._promiseFulfilled(maybePromise._value(), i);
+ } else {
+ this._promiseRejected(maybePromise._reason(), i);
+ }
+ } else if (!isResolved) {
+ this._promiseFulfilled(maybePromise, i);
+ }
+ }
+};
+
+PromiseArray.prototype._isResolved = function () {
+ return this._values === null;
+};
+
+PromiseArray.prototype._resolve = function (value) {
+ this._values = null;
+ this._promise._fulfill(value);
+};
+
+PromiseArray.prototype.__hardReject__ =
+PromiseArray.prototype._reject = function (reason) {
+ this._values = null;
+ this._promise._rejectCallback(reason, false, true);
+};
+
+PromiseArray.prototype._promiseProgressed = function (progressValue, index) {
+ this._promise._progress({
+ index: index,
+ value: progressValue
+ });
+};
+
+
+PromiseArray.prototype._promiseFulfilled = function (value, index) {
+ this._values[index] = value;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ this._resolve(this._values);
+ }
+};
+
+PromiseArray.prototype._promiseRejected = function (reason, index) {
+ this._totalResolved++;
+ this._reject(reason);
+};
+
+PromiseArray.prototype.shouldCopyValues = function () {
+ return true;
+};
+
+PromiseArray.prototype.getActualLength = function (len) {
+ return len;
+};
+
+return PromiseArray;
+};
+
+},{"./util.js":38}],25:[function(_dereq_,module,exports){
+"use strict";
+var util = _dereq_("./util.js");
+var maybeWrapAsError = util.maybeWrapAsError;
+var errors = _dereq_("./errors.js");
+var TimeoutError = errors.TimeoutError;
+var OperationalError = errors.OperationalError;
+var haveGetters = util.haveGetters;
+var es5 = _dereq_("./es5.js");
+
+function isUntypedError(obj) {
+ return obj instanceof Error &&
+ es5.getPrototypeOf(obj) === Error.prototype;
+}
+
+var rErrorKey = /^(?:name|message|stack|cause)$/;
+function wrapAsOperationalError(obj) {
+ var ret;
+ if (isUntypedError(obj)) {
+ ret = new OperationalError(obj);
+ ret.name = obj.name;
+ ret.message = obj.message;
+ ret.stack = obj.stack;
+ var keys = es5.keys(obj);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (!rErrorKey.test(key)) {
+ ret[key] = obj[key];
+ }
+ }
+ return ret;
+ }
+ util.markAsOriginatingFromRejection(obj);
+ return obj;
+}
+
+function nodebackForPromise(promise) {
+ return function(err, value) {
+ if (promise === null) return;
+
+ if (err) {
+ var wrapped = wrapAsOperationalError(maybeWrapAsError(err));
+ promise._attachExtraTrace(wrapped);
+ promise._reject(wrapped);
+ } else if (arguments.length > 2) {
+ var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];}
+ promise._fulfill(args);
+ } else {
+ promise._fulfill(value);
+ }
+
+ promise = null;
+ };
+}
+
+
+var PromiseResolver;
+if (!haveGetters) {
+ PromiseResolver = function (promise) {
+ this.promise = promise;
+ this.asCallback = nodebackForPromise(promise);
+ this.callback = this.asCallback;
+ };
+}
+else {
+ PromiseResolver = function (promise) {
+ this.promise = promise;
+ };
+}
+if (haveGetters) {
+ var prop = {
+ get: function() {
+ return nodebackForPromise(this.promise);
+ }
+ };
+ es5.defineProperty(PromiseResolver.prototype, "asCallback", prop);
+ es5.defineProperty(PromiseResolver.prototype, "callback", prop);
+}
+
+PromiseResolver._nodebackForPromise = nodebackForPromise;
+
+PromiseResolver.prototype.toString = function () {
+ return "[object PromiseResolver]";
+};
+
+PromiseResolver.prototype.resolve =
+PromiseResolver.prototype.fulfill = function (value) {
+ if (!(this instanceof PromiseResolver)) {
+ throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\u000a\u000a See http://goo.gl/sdkXL9\u000a");
+ }
+ this.promise._resolveCallback(value);
+};
+
+PromiseResolver.prototype.reject = function (reason) {
+ if (!(this instanceof PromiseResolver)) {
+ throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\u000a\u000a See http://goo.gl/sdkXL9\u000a");
+ }
+ this.promise._rejectCallback(reason);
+};
+
+PromiseResolver.prototype.progress = function (value) {
+ if (!(this instanceof PromiseResolver)) {
+ throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\u000a\u000a See http://goo.gl/sdkXL9\u000a");
+ }
+ this.promise._progress(value);
+};
+
+PromiseResolver.prototype.cancel = function (err) {
+ this.promise.cancel(err);
+};
+
+PromiseResolver.prototype.timeout = function () {
+ this.reject(new TimeoutError("timeout"));
+};
+
+PromiseResolver.prototype.isResolved = function () {
+ return this.promise.isResolved();
+};
+
+PromiseResolver.prototype.toJSON = function () {
+ return this.promise.toJSON();
+};
+
+module.exports = PromiseResolver;
+
+},{"./errors.js":13,"./es5.js":14,"./util.js":38}],26:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var THIS = {};
+var util = _dereq_("./util.js");
+var nodebackForPromise = _dereq_("./promise_resolver.js")
+ ._nodebackForPromise;
+var withAppended = util.withAppended;
+var maybeWrapAsError = util.maybeWrapAsError;
+var canEvaluate = util.canEvaluate;
+var TypeError = _dereq_("./errors").TypeError;
+var defaultSuffix = "Async";
+var defaultPromisified = {__isPromisified__: true};
+var noCopyPropsPattern =
+ /^(?:length|name|arguments|caller|callee|prototype|__isPromisified__)$/;
+var defaultFilter = function(name, func) {
+ return util.isIdentifier(name) &&
+ name.charAt(0) !== "_" &&
+ !util.isClass(func);
+};
+
+function propsFilter(key) {
+ return !noCopyPropsPattern.test(key);
+}
+
+function isPromisified(fn) {
+ try {
+ return fn.__isPromisified__ === true;
+ }
+ catch (e) {
+ return false;
+ }
+}
+
+function hasPromisified(obj, key, suffix) {
+ var val = util.getDataPropertyOrDefault(obj, key + suffix,
+ defaultPromisified);
+ return val ? isPromisified(val) : false;
+}
+function checkValid(ret, suffix, suffixRegexp) {
+ for (var i = 0; i < ret.length; i += 2) {
+ var key = ret[i];
+ if (suffixRegexp.test(key)) {
+ var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
+ for (var j = 0; j < ret.length; j += 2) {
+ if (ret[j] === keyWithoutAsyncSuffix) {
+ throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a See http://goo.gl/iWrZbw\u000a"
+ .replace("%s", suffix));
+ }
+ }
+ }
+ }
+}
+
+function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
+ var keys = util.inheritedDataKeys(obj);
+ var ret = [];
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ var value = obj[key];
+ var passesDefaultFilter = filter === defaultFilter
+ ? true : defaultFilter(key, value, obj);
+ if (typeof value === "function" &&
+ !isPromisified(value) &&
+ !hasPromisified(obj, key, suffix) &&
+ filter(key, value, obj, passesDefaultFilter)) {
+ ret.push(key, value);
+ }
+ }
+ checkValid(ret, suffix, suffixRegexp);
+ return ret;
+}
+
+var escapeIdentRegex = function(str) {
+ return str.replace(/([$])/, "\\$");
+};
+
+var makeNodePromisifiedEval;
+if (!true) {
+var switchCaseArgumentOrder = function(likelyArgumentCount) {
+ var ret = [likelyArgumentCount];
+ var min = Math.max(0, likelyArgumentCount - 1 - 3);
+ for(var i = likelyArgumentCount - 1; i >= min; --i) {
+ ret.push(i);
+ }
+ for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
+ ret.push(i);
+ }
+ return ret;
+};
+
+var argumentSequence = function(argumentCount) {
+ return util.filledRange(argumentCount, "_arg", "");
+};
+
+var parameterDeclaration = function(parameterCount) {
+ return util.filledRange(
+ Math.max(parameterCount, 3), "_arg", "");
+};
+
+var parameterCount = function(fn) {
+ if (typeof fn.length === "number") {
+ return Math.max(Math.min(fn.length, 1023 + 1), 0);
+ }
+ return 0;
+};
+
+makeNodePromisifiedEval =
+function(callback, receiver, originalName, fn) {
+ var newParameterCount = Math.max(0, parameterCount(fn) - 1);
+ var argumentOrder = switchCaseArgumentOrder(newParameterCount);
+ var shouldProxyThis = typeof callback === "string" || receiver === THIS;
+
+ function generateCallForArgumentCount(count) {
+ var args = argumentSequence(count).join(", ");
+ var comma = count > 0 ? ", " : "";
+ var ret;
+ if (shouldProxyThis) {
+ ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
+ } else {
+ ret = receiver === undefined
+ ? "ret = callback({{args}}, nodeback); break;\n"
+ : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
+ }
+ return ret.replace("{{args}}", args).replace(", ", comma);
+ }
+
+ function generateArgumentSwitchCase() {
+ var ret = "";
+ for (var i = 0; i < argumentOrder.length; ++i) {
+ ret += "case " + argumentOrder[i] +":" +
+ generateCallForArgumentCount(argumentOrder[i]);
+ }
+
+ ret += " \n\
+ default: \n\
+ var args = new Array(len + 1); \n\
+ var i = 0; \n\
+ for (var i = 0; i < len; ++i) { \n\
+ args[i] = arguments[i]; \n\
+ } \n\
+ args[i] = nodeback; \n\
+ [CodeForCall] \n\
+ break; \n\
+ ".replace("[CodeForCall]", (shouldProxyThis
+ ? "ret = callback.apply(this, args);\n"
+ : "ret = callback.apply(receiver, args);\n"));
+ return ret;
+ }
+
+ var getFunctionCode = typeof callback === "string"
+ ? ("this != null ? this['"+callback+"'] : fn")
+ : "fn";
+
+ return new Function("Promise",
+ "fn",
+ "receiver",
+ "withAppended",
+ "maybeWrapAsError",
+ "nodebackForPromise",
+ "tryCatch",
+ "errorObj",
+ "INTERNAL","'use strict'; \n\
+ var ret = function (Parameters) { \n\
+ 'use strict'; \n\
+ var len = arguments.length; \n\
+ var promise = new Promise(INTERNAL); \n\
+ promise._captureStackTrace(); \n\
+ var nodeback = nodebackForPromise(promise); \n\
+ var ret; \n\
+ var callback = tryCatch([GetFunctionCode]); \n\
+ switch(len) { \n\
+ [CodeForSwitchCase] \n\
+ } \n\
+ if (ret === errorObj) { \n\
+ promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
+ } \n\
+ return promise; \n\
+ }; \n\
+ ret.__isPromisified__ = true; \n\
+ return ret; \n\
+ "
+ .replace("Parameters", parameterDeclaration(newParameterCount))
+ .replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
+ .replace("[GetFunctionCode]", getFunctionCode))(
+ Promise,
+ fn,
+ receiver,
+ withAppended,
+ maybeWrapAsError,
+ nodebackForPromise,
+ util.tryCatch,
+ util.errorObj,
+ INTERNAL
+ );
+};
+}
+
+function makeNodePromisifiedClosure(callback, receiver, _, fn) {
+ var defaultThis = (function() {return this;})();
+ var method = callback;
+ if (typeof method === "string") {
+ callback = fn;
+ }
+ function promisified() {
+ var _receiver = receiver;
+ if (receiver === THIS) _receiver = this;
+ var promise = new Promise(INTERNAL);
+ promise._captureStackTrace();
+ var cb = typeof method === "string" && this !== defaultThis
+ ? this[method] : callback;
+ var fn = nodebackForPromise(promise);
+ try {
+ cb.apply(_receiver, withAppended(arguments, fn));
+ } catch(e) {
+ promise._rejectCallback(maybeWrapAsError(e), true, true);
+ }
+ return promise;
+ }
+ promisified.__isPromisified__ = true;
+ return promisified;
+}
+
+var makeNodePromisified = canEvaluate
+ ? makeNodePromisifiedEval
+ : makeNodePromisifiedClosure;
+
+function promisifyAll(obj, suffix, filter, promisifier) {
+ var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
+ var methods =
+ promisifiableMethods(obj, suffix, suffixRegexp, filter);
+
+ for (var i = 0, len = methods.length; i < len; i+= 2) {
+ var key = methods[i];
+ var fn = methods[i+1];
+ var promisifiedKey = key + suffix;
+ obj[promisifiedKey] = promisifier === makeNodePromisified
+ ? makeNodePromisified(key, THIS, key, fn, suffix)
+ : promisifier(fn, function() {
+ return makeNodePromisified(key, THIS, key, fn, suffix);
+ });
+ }
+ util.toFastProperties(obj);
+ return obj;
+}
+
+function promisify(callback, receiver) {
+ return makeNodePromisified(callback, receiver, undefined, callback);
+}
+
+Promise.promisify = function (fn, receiver) {
+ if (typeof fn !== "function") {
+ throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ }
+ if (isPromisified(fn)) {
+ return fn;
+ }
+ var ret = promisify(fn, arguments.length < 2 ? THIS : receiver);
+ util.copyDescriptors(fn, ret, propsFilter);
+ return ret;
+};
+
+Promise.promisifyAll = function (target, options) {
+ if (typeof target !== "function" && typeof target !== "object") {
+ throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a See http://goo.gl/9ITlV0\u000a");
+ }
+ options = Object(options);
+ var suffix = options.suffix;
+ if (typeof suffix !== "string") suffix = defaultSuffix;
+ var filter = options.filter;
+ if (typeof filter !== "function") filter = defaultFilter;
+ var promisifier = options.promisifier;
+ if (typeof promisifier !== "function") promisifier = makeNodePromisified;
+
+ if (!util.isIdentifier(suffix)) {
+ throw new RangeError("suffix must be a valid identifier\u000a\u000a See http://goo.gl/8FZo5V\u000a");
+ }
+
+ var keys = util.inheritedDataKeys(target);
+ for (var i = 0; i < keys.length; ++i) {
+ var value = target[keys[i]];
+ if (keys[i] !== "constructor" &&
+ util.isClass(value)) {
+ promisifyAll(value.prototype, suffix, filter, promisifier);
+ promisifyAll(value, suffix, filter, promisifier);
+ }
+ }
+
+ return promisifyAll(target, suffix, filter, promisifier);
+};
+};
+
+
+},{"./errors":13,"./promise_resolver.js":25,"./util.js":38}],27:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(
+ Promise, PromiseArray, tryConvertToPromise, apiRejection) {
+var util = _dereq_("./util.js");
+var isObject = util.isObject;
+var es5 = _dereq_("./es5.js");
+
+function PropertiesPromiseArray(obj) {
+ var keys = es5.keys(obj);
+ var len = keys.length;
+ var values = new Array(len * 2);
+ for (var i = 0; i < len; ++i) {
+ var key = keys[i];
+ values[i] = obj[key];
+ values[i + len] = key;
+ }
+ this.constructor$(values);
+}
+util.inherits(PropertiesPromiseArray, PromiseArray);
+
+PropertiesPromiseArray.prototype._init = function () {
+ this._init$(undefined, -3) ;
+};
+
+PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ this._values[index] = value;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ var val = {};
+ var keyOffset = this.length();
+ for (var i = 0, len = this.length(); i < len; ++i) {
+ val[this._values[i + keyOffset]] = this._values[i];
+ }
+ this._resolve(val);
+ }
+};
+
+PropertiesPromiseArray.prototype._promiseProgressed = function (value, index) {
+ this._promise._progress({
+ key: this._values[index + this.length()],
+ value: value
+ });
+};
+
+PropertiesPromiseArray.prototype.shouldCopyValues = function () {
+ return false;
+};
+
+PropertiesPromiseArray.prototype.getActualLength = function (len) {
+ return len >> 1;
+};
+
+function props(promises) {
+ var ret;
+ var castValue = tryConvertToPromise(promises);
+
+ if (!isObject(castValue)) {
+ return apiRejection("cannot await properties of a non-object\u000a\u000a See http://goo.gl/OsFKC8\u000a");
+ } else if (castValue instanceof Promise) {
+ ret = castValue._then(
+ Promise.props, undefined, undefined, undefined, undefined);
+ } else {
+ ret = new PropertiesPromiseArray(castValue).promise();
+ }
+
+ if (castValue instanceof Promise) {
+ ret._propagateFrom(castValue, 4);
+ }
+ return ret;
+}
+
+Promise.prototype.props = function () {
+ return props(this);
+};
+
+Promise.props = function (promises) {
+ return props(promises);
+};
+};
+
+},{"./es5.js":14,"./util.js":38}],28:[function(_dereq_,module,exports){
+"use strict";
+function arrayMove(src, srcIndex, dst, dstIndex, len) {
+ for (var j = 0; j < len; ++j) {
+ dst[j + dstIndex] = src[j + srcIndex];
+ src[j + srcIndex] = void 0;
+ }
+}
+
+function Queue(capacity) {
+ this._capacity = capacity;
+ this._length = 0;
+ this._front = 0;
+}
+
+Queue.prototype._willBeOverCapacity = function (size) {
+ return this._capacity < size;
+};
+
+Queue.prototype._pushOne = function (arg) {
+ var length = this.length();
+ this._checkCapacity(length + 1);
+ var i = (this._front + length) & (this._capacity - 1);
+ this[i] = arg;
+ this._length = length + 1;
+};
+
+Queue.prototype._unshiftOne = function(value) {
+ var capacity = this._capacity;
+ this._checkCapacity(this.length() + 1);
+ var front = this._front;
+ var i = (((( front - 1 ) &
+ ( capacity - 1) ) ^ capacity ) - capacity );
+ this[i] = value;
+ this._front = i;
+ this._length = this.length() + 1;
+};
+
+Queue.prototype.unshift = function(fn, receiver, arg) {
+ this._unshiftOne(arg);
+ this._unshiftOne(receiver);
+ this._unshiftOne(fn);
+};
+
+Queue.prototype.push = function (fn, receiver, arg) {
+ var length = this.length() + 3;
+ if (this._willBeOverCapacity(length)) {
+ this._pushOne(fn);
+ this._pushOne(receiver);
+ this._pushOne(arg);
+ return;
+ }
+ var j = this._front + length - 3;
+ this._checkCapacity(length);
+ var wrapMask = this._capacity - 1;
+ this[(j + 0) & wrapMask] = fn;
+ this[(j + 1) & wrapMask] = receiver;
+ this[(j + 2) & wrapMask] = arg;
+ this._length = length;
+};
+
+Queue.prototype.shift = function () {
+ var front = this._front,
+ ret = this[front];
+
+ this[front] = undefined;
+ this._front = (front + 1) & (this._capacity - 1);
+ this._length--;
+ return ret;
+};
+
+Queue.prototype.length = function () {
+ return this._length;
+};
+
+Queue.prototype._checkCapacity = function (size) {
+ if (this._capacity < size) {
+ this._resizeTo(this._capacity << 1);
+ }
+};
+
+Queue.prototype._resizeTo = function (capacity) {
+ var oldCapacity = this._capacity;
+ this._capacity = capacity;
+ var front = this._front;
+ var length = this._length;
+ var moveItemsCount = (front + length) & (oldCapacity - 1);
+ arrayMove(this, 0, this, oldCapacity, moveItemsCount);
+};
+
+module.exports = Queue;
+
+},{}],29:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(
+ Promise, INTERNAL, tryConvertToPromise, apiRejection) {
+var isArray = _dereq_("./util.js").isArray;
+
+var raceLater = function (promise) {
+ return promise.then(function(array) {
+ return race(array, promise);
+ });
+};
+
+function race(promises, parent) {
+ var maybePromise = tryConvertToPromise(promises);
+
+ if (maybePromise instanceof Promise) {
+ return raceLater(maybePromise);
+ } else if (!isArray(promises)) {
+ return apiRejection("expecting an array, a promise or a thenable\u000a\u000a See http://goo.gl/s8MMhc\u000a");
+ }
+
+ var ret = new Promise(INTERNAL);
+ if (parent !== undefined) {
+ ret._propagateFrom(parent, 4 | 1);
+ }
+ var fulfill = ret._fulfill;
+ var reject = ret._reject;
+ for (var i = 0, len = promises.length; i < len; ++i) {
+ var val = promises[i];
+
+ if (val === undefined && !(i in promises)) {
+ continue;
+ }
+
+ Promise.cast(val)._then(fulfill, reject, undefined, ret, null);
+ }
+ return ret;
+}
+
+Promise.race = function (promises) {
+ return race(promises, undefined);
+};
+
+Promise.prototype.race = function () {
+ return race(this, undefined);
+};
+
+};
+
+},{"./util.js":38}],30:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise,
+ PromiseArray,
+ apiRejection,
+ tryConvertToPromise,
+ INTERNAL) {
+var async = _dereq_("./async.js");
+var util = _dereq_("./util.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+function ReductionPromiseArray(promises, fn, accum, _each) {
+ this.constructor$(promises);
+ this._promise._captureStackTrace();
+ this._preservedValues = _each === INTERNAL ? [] : null;
+ this._zerothIsAccum = (accum === undefined);
+ this._gotAccum = false;
+ this._reducingIndex = (this._zerothIsAccum ? 1 : 0);
+ this._valuesPhase = undefined;
+ var maybePromise = tryConvertToPromise(accum, this._promise);
+ var rejected = false;
+ var isPromise = maybePromise instanceof Promise;
+ if (isPromise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ maybePromise._proxyPromiseArray(this, -1);
+ } else if (maybePromise._isFulfilled()) {
+ accum = maybePromise._value();
+ this._gotAccum = true;
+ } else {
+ this._reject(maybePromise._reason());
+ rejected = true;
+ }
+ }
+ if (!(isPromise || this._zerothIsAccum)) this._gotAccum = true;
+ this._callback = fn;
+ this._accum = accum;
+ if (!rejected) async.invoke(init, this, undefined);
+}
+function init() {
+ this._init$(undefined, -5);
+}
+util.inherits(ReductionPromiseArray, PromiseArray);
+
+ReductionPromiseArray.prototype._init = function () {};
+
+ReductionPromiseArray.prototype._resolveEmptyArray = function () {
+ if (this._gotAccum || this._zerothIsAccum) {
+ this._resolve(this._preservedValues !== null
+ ? [] : this._accum);
+ }
+};
+
+ReductionPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ var values = this._values;
+ values[index] = value;
+ var length = this.length();
+ var preservedValues = this._preservedValues;
+ var isEach = preservedValues !== null;
+ var gotAccum = this._gotAccum;
+ var valuesPhase = this._valuesPhase;
+ var valuesPhaseIndex;
+ if (!valuesPhase) {
+ valuesPhase = this._valuesPhase = new Array(length);
+ for (valuesPhaseIndex=0; valuesPhaseIndex<length; ++valuesPhaseIndex) {
+ valuesPhase[valuesPhaseIndex] = 0;
+ }
+ }
+ valuesPhaseIndex = valuesPhase[index];
+
+ if (index === 0 && this._zerothIsAccum) {
+ this._accum = value;
+ this._gotAccum = gotAccum = true;
+ valuesPhase[index] = ((valuesPhaseIndex === 0)
+ ? 1 : 2);
+ } else if (index === -1) {
+ this._accum = value;
+ this._gotAccum = gotAccum = true;
+ } else {
+ if (valuesPhaseIndex === 0) {
+ valuesPhase[index] = 1;
+ } else {
+ valuesPhase[index] = 2;
+ this._accum = value;
+ }
+ }
+ if (!gotAccum) return;
+
+ var callback = this._callback;
+ var receiver = this._promise._boundTo;
+ var ret;
+
+ for (var i = this._reducingIndex; i < length; ++i) {
+ valuesPhaseIndex = valuesPhase[i];
+ if (valuesPhaseIndex === 2) {
+ this._reducingIndex = i + 1;
+ continue;
+ }
+ if (valuesPhaseIndex !== 1) return;
+ value = values[i];
+ this._promise._pushContext();
+ if (isEach) {
+ preservedValues.push(value);
+ ret = tryCatch(callback).call(receiver, value, i, length);
+ }
+ else {
+ ret = tryCatch(callback)
+ .call(receiver, this._accum, value, i, length);
+ }
+ this._promise._popContext();
+
+ if (ret === errorObj) return this._reject(ret.e);
+
+ var maybePromise = tryConvertToPromise(ret, this._promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ valuesPhase[i] = 4;
+ return maybePromise._proxyPromiseArray(this, i);
+ } else if (maybePromise._isFulfilled()) {
+ ret = maybePromise._value();
+ } else {
+ return this._reject(maybePromise._reason());
+ }
+ }
+
+ this._reducingIndex = i + 1;
+ this._accum = ret;
+ }
+
+ this._resolve(isEach ? preservedValues : this._accum);
+};
+
+function reduce(promises, fn, initialValue, _each) {
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
+ return array.promise();
+}
+
+Promise.prototype.reduce = function (fn, initialValue) {
+ return reduce(this, fn, initialValue, null);
+};
+
+Promise.reduce = function (promises, fn, initialValue, _each) {
+ return reduce(promises, fn, initialValue, _each);
+};
+};
+
+},{"./async.js":2,"./util.js":38}],31:[function(_dereq_,module,exports){
+"use strict";
+var schedule;
+var noAsyncScheduler = function() {
+ throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/m3OTXk\u000a");
+};
+if (_dereq_("./util.js").isNode) {
+ var version = process.versions.node.split(".").map(Number);
+ schedule = (version[0] === 0 && version[1] > 10) || (version[0] > 0)
+ ? global.setImmediate : process.nextTick;
+
+ if (!schedule) {
+ if (typeof setImmediate !== "undefined") {
+ schedule = setImmediate;
+ } else if (typeof setTimeout !== "undefined") {
+ schedule = setTimeout;
+ } else {
+ schedule = noAsyncScheduler;
+ }
+ }
+} else if (typeof MutationObserver !== "undefined") {
+ schedule = function(fn) {
+ var div = document.createElement("div");
+ var observer = new MutationObserver(fn);
+ observer.observe(div, {attributes: true});
+ return function() { div.classList.toggle("foo"); };
+ };
+ schedule.isStatic = true;
+} else if (typeof setImmediate !== "undefined") {
+ schedule = function (fn) {
+ setImmediate(fn);
+ };
+} else if (typeof setTimeout !== "undefined") {
+ schedule = function (fn) {
+ setTimeout(fn, 0);
+ };
+} else {
+ schedule = noAsyncScheduler;
+}
+module.exports = schedule;
+
+},{"./util.js":38}],32:[function(_dereq_,module,exports){
+"use strict";
+module.exports =
+ function(Promise, PromiseArray) {
+var PromiseInspection = Promise.PromiseInspection;
+var util = _dereq_("./util.js");
+
+function SettledPromiseArray(values) {
+ this.constructor$(values);
+}
+util.inherits(SettledPromiseArray, PromiseArray);
+
+SettledPromiseArray.prototype._promiseResolved = function (index, inspection) {
+ this._values[index] = inspection;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ this._resolve(this._values);
+ }
+};
+
+SettledPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ var ret = new PromiseInspection();
+ ret._bitField = 268435456;
+ ret._settledValue = value;
+ this._promiseResolved(index, ret);
+};
+SettledPromiseArray.prototype._promiseRejected = function (reason, index) {
+ var ret = new PromiseInspection();
+ ret._bitField = 134217728;
+ ret._settledValue = reason;
+ this._promiseResolved(index, ret);
+};
+
+Promise.settle = function (promises) {
+ return new SettledPromiseArray(promises).promise();
+};
+
+Promise.prototype.settle = function () {
+ return new SettledPromiseArray(this).promise();
+};
+};
+
+},{"./util.js":38}],33:[function(_dereq_,module,exports){
+"use strict";
+module.exports =
+function(Promise, PromiseArray, apiRejection) {
+var util = _dereq_("./util.js");
+var RangeError = _dereq_("./errors.js").RangeError;
+var AggregateError = _dereq_("./errors.js").AggregateError;
+var isArray = util.isArray;
+
+
+function SomePromiseArray(values) {
+ this.constructor$(values);
+ this._howMany = 0;
+ this._unwrap = false;
+ this._initialized = false;
+}
+util.inherits(SomePromiseArray, PromiseArray);
+
+SomePromiseArray.prototype._init = function () {
+ if (!this._initialized) {
+ return;
+ }
+ if (this._howMany === 0) {
+ this._resolve([]);
+ return;
+ }
+ this._init$(undefined, -5);
+ var isArrayResolved = isArray(this._values);
+ if (!this._isResolved() &&
+ isArrayResolved &&
+ this._howMany > this._canPossiblyFulfill()) {
+ this._reject(this._getRangeError(this.length()));
+ }
+};
+
+SomePromiseArray.prototype.init = function () {
+ this._initialized = true;
+ this._init();
+};
+
+SomePromiseArray.prototype.setUnwrap = function () {
+ this._unwrap = true;
+};
+
+SomePromiseArray.prototype.howMany = function () {
+ return this._howMany;
+};
+
+SomePromiseArray.prototype.setHowMany = function (count) {
+ this._howMany = count;
+};
+
+SomePromiseArray.prototype._promiseFulfilled = function (value) {
+ this._addFulfilled(value);
+ if (this._fulfilled() === this.howMany()) {
+ this._values.length = this.howMany();
+ if (this.howMany() === 1 && this._unwrap) {
+ this._resolve(this._values[0]);
+ } else {
+ this._resolve(this._values);
+ }
+ }
+
+};
+SomePromiseArray.prototype._promiseRejected = function (reason) {
+ this._addRejected(reason);
+ if (this.howMany() > this._canPossiblyFulfill()) {
+ var e = new AggregateError();
+ for (var i = this.length(); i < this._values.length; ++i) {
+ e.push(this._values[i]);
+ }
+ this._reject(e);
+ }
+};
+
+SomePromiseArray.prototype._fulfilled = function () {
+ return this._totalResolved;
+};
+
+SomePromiseArray.prototype._rejected = function () {
+ return this._values.length - this.length();
+};
+
+SomePromiseArray.prototype._addRejected = function (reason) {
+ this._values.push(reason);
+};
+
+SomePromiseArray.prototype._addFulfilled = function (value) {
+ this._values[this._totalResolved++] = value;
+};
+
+SomePromiseArray.prototype._canPossiblyFulfill = function () {
+ return this.length() - this._rejected();
+};
+
+SomePromiseArray.prototype._getRangeError = function (count) {
+ var message = "Input array must contain at least " +
+ this._howMany + " items but contains only " + count + " items";
+ return new RangeError(message);
+};
+
+SomePromiseArray.prototype._resolveEmptyArray = function () {
+ this._reject(this._getRangeError(0));
+};
+
+function some(promises, howMany) {
+ if ((howMany | 0) !== howMany || howMany < 0) {
+ return apiRejection("expecting a positive integer\u000a\u000a See http://goo.gl/1wAmHx\u000a");
+ }
+ var ret = new SomePromiseArray(promises);
+ var promise = ret.promise();
+ ret.setHowMany(howMany);
+ ret.init();
+ return promise;
+}
+
+Promise.some = function (promises, howMany) {
+ return some(promises, howMany);
+};
+
+Promise.prototype.some = function (howMany) {
+ return some(this, howMany);
+};
+
+Promise._SomePromiseArray = SomePromiseArray;
+};
+
+},{"./errors.js":13,"./util.js":38}],34:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise) {
+function PromiseInspection(promise) {
+ if (promise !== undefined) {
+ promise = promise._target();
+ this._bitField = promise._bitField;
+ this._settledValue = promise._settledValue;
+ }
+ else {
+ this._bitField = 0;
+ this._settledValue = undefined;
+ }
+}
+
+PromiseInspection.prototype.value = function () {
+ if (!this.isFulfilled()) {
+ throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\u000a\u000a See http://goo.gl/hc1DLj\u000a");
+ }
+ return this._settledValue;
+};
+
+PromiseInspection.prototype.error =
+PromiseInspection.prototype.reason = function () {
+ if (!this.isRejected()) {
+ throw new TypeError("cannot get rejection reason of a non-rejected promise\u000a\u000a See http://goo.gl/hPuiwB\u000a");
+ }
+ return this._settledValue;
+};
+
+PromiseInspection.prototype.isFulfilled =
+Promise.prototype._isFulfilled = function () {
+ return (this._bitField & 268435456) > 0;
+};
+
+PromiseInspection.prototype.isRejected =
+Promise.prototype._isRejected = function () {
+ return (this._bitField & 134217728) > 0;
+};
+
+PromiseInspection.prototype.isPending =
+Promise.prototype._isPending = function () {
+ return (this._bitField & 402653184) === 0;
+};
+
+PromiseInspection.prototype.isResolved =
+Promise.prototype._isResolved = function () {
+ return (this._bitField & 402653184) > 0;
+};
+
+Promise.prototype.isPending = function() {
+ return this._target()._isPending();
+};
+
+Promise.prototype.isRejected = function() {
+ return this._target()._isRejected();
+};
+
+Promise.prototype.isFulfilled = function() {
+ return this._target()._isFulfilled();
+};
+
+Promise.prototype.isResolved = function() {
+ return this._target()._isResolved();
+};
+
+Promise.prototype._value = function() {
+ return this._settledValue;
+};
+
+Promise.prototype._reason = function() {
+ this._unsetRejectionIsUnhandled();
+ return this._settledValue;
+};
+
+Promise.prototype.value = function() {
+ var target = this._target();
+ if (!target.isFulfilled()) {
+ throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\u000a\u000a See http://goo.gl/hc1DLj\u000a");
+ }
+ return target._settledValue;
+};
+
+Promise.prototype.reason = function() {
+ var target = this._target();
+ if (!target.isRejected()) {
+ throw new TypeError("cannot get rejection reason of a non-rejected promise\u000a\u000a See http://goo.gl/hPuiwB\u000a");
+ }
+ target._unsetRejectionIsUnhandled();
+ return target._settledValue;
+};
+
+
+Promise.PromiseInspection = PromiseInspection;
+};
+
+},{}],35:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var util = _dereq_("./util.js");
+var errorObj = util.errorObj;
+var isObject = util.isObject;
+
+function tryConvertToPromise(obj, context) {
+ if (isObject(obj)) {
+ if (obj instanceof Promise) {
+ return obj;
+ }
+ else if (isAnyBluebirdPromise(obj)) {
+ var ret = new Promise(INTERNAL);
+ obj._then(
+ ret._fulfillUnchecked,
+ ret._rejectUncheckedCheckError,
+ ret._progressUnchecked,
+ ret,
+ null
+ );
+ return ret;
+ }
+ var then = util.tryCatch(getThen)(obj);
+ if (then === errorObj) {
+ if (context) context._pushContext();
+ var ret = Promise.reject(then.e);
+ if (context) context._popContext();
+ return ret;
+ } else if (typeof then === "function") {
+ return doThenable(obj, then, context);
+ }
+ }
+ return obj;
+}
+
+function getThen(obj) {
+ return obj.then;
+}
+
+var hasProp = {}.hasOwnProperty;
+function isAnyBluebirdPromise(obj) {
+ return hasProp.call(obj, "_promise0");
+}
+
+function doThenable(x, then, context) {
+ var promise = new Promise(INTERNAL);
+ var ret = promise;
+ if (context) context._pushContext();
+ promise._captureStackTrace();
+ if (context) context._popContext();
+ var synchronous = true;
+ var result = util.tryCatch(then).call(x,
+ resolveFromThenable,
+ rejectFromThenable,
+ progressFromThenable);
+ synchronous = false;
+ if (promise && result === errorObj) {
+ promise._rejectCallback(result.e, true, true);
+ promise = null;
+ }
+
+ function resolveFromThenable(value) {
+ if (!promise) return;
+ if (x === value) {
+ promise._rejectCallback(
+ Promise._makeSelfResolutionError(), false, true);
+ } else {
+ promise._resolveCallback(value);
+ }
+ promise = null;
+ }
+
+ function rejectFromThenable(reason) {
+ if (!promise) return;
+ promise._rejectCallback(reason, synchronous, true);
+ promise = null;
+ }
+
+ function progressFromThenable(value) {
+ if (!promise) return;
+ if (typeof promise._progress === "function") {
+ promise._progress(value);
+ }
+ }
+ return ret;
+}
+
+return tryConvertToPromise;
+};
+
+},{"./util.js":38}],36:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var util = _dereq_("./util.js");
+var TimeoutError = Promise.TimeoutError;
+
+var afterTimeout = function (promise, message) {
+ if (!promise.isPending()) return;
+ if (typeof message !== "string") {
+ message = "operation timed out";
+ }
+ var err = new TimeoutError(message);
+ util.markAsOriginatingFromRejection(err);
+ promise._attachExtraTrace(err);
+ promise._cancel(err);
+};
+
+var afterValue = function(value) { return delay(+this).thenReturn(value); };
+var delay = Promise.delay = function (value, ms) {
+ if (ms === undefined) {
+ ms = value;
+ value = undefined;
+ var ret = new Promise(INTERNAL);
+ setTimeout(function() { ret._fulfill(); }, ms);
+ return ret;
+ }
+ ms = +ms;
+ return Promise.resolve(value)._then(afterValue, null, null, ms, undefined);
+};
+
+Promise.prototype.delay = function (ms) {
+ return delay(this, ms);
+};
+
+function successClear(value) {
+ var handle = this;
+ if (handle instanceof Number) handle = +handle;
+ clearTimeout(handle);
+ return value;
+}
+
+function failureClear(reason) {
+ var handle = this;
+ if (handle instanceof Number) handle = +handle;
+ clearTimeout(handle);
+ throw reason;
+}
+
+Promise.prototype.timeout = function (ms, message) {
+ ms = +ms;
+ var ret = this.then().cancellable();
+ ret._cancellationParent = this;
+ var handle = setTimeout(function timeoutTimeout() {
+ afterTimeout(ret, message);
+ }, ms);
+ return ret._then(successClear, failureClear, undefined, handle, undefined);
+};
+
+};
+
+},{"./util.js":38}],37:[function(_dereq_,module,exports){
+"use strict";
+module.exports = function (Promise, apiRejection, tryConvertToPromise,
+ createContext) {
+ var TypeError = _dereq_("./errors.js").TypeError;
+ var inherits = _dereq_("./util.js").inherits;
+ var PromiseInspection = Promise.PromiseInspection;
+
+ function inspectionMapper(inspections) {
+ var len = inspections.length;
+ for (var i = 0; i < len; ++i) {
+ var inspection = inspections[i];
+ if (inspection.isRejected()) {
+ return Promise.reject(inspection.error());
+ }
+ inspections[i] = inspection._settledValue;
+ }
+ return inspections;
+ }
+
+ function thrower(e) {
+ setTimeout(function(){throw e;}, 0);
+ }
+
+ function castPreservingDisposable(thenable) {
+ var maybePromise = tryConvertToPromise(thenable);
+ if (maybePromise !== thenable &&
+ typeof thenable._isDisposable === "function" &&
+ typeof thenable._getDisposer === "function" &&
+ thenable._isDisposable()) {
+ maybePromise._setDisposable(thenable._getDisposer());
+ }
+ return maybePromise;
+ }
+ function dispose(resources, inspection) {
+ var i = 0;
+ var len = resources.length;
+ var ret = Promise.defer();
+ function iterator() {
+ if (i >= len) return ret.resolve();
+ var maybePromise = castPreservingDisposable(resources[i++]);
+ if (maybePromise instanceof Promise &&
+ maybePromise._isDisposable()) {
+ try {
+ maybePromise = tryConvertToPromise(
+ maybePromise._getDisposer().tryDispose(inspection),
+ resources.promise);
+ } catch (e) {
+ return thrower(e);
+ }
+ if (maybePromise instanceof Promise) {
+ return maybePromise._then(iterator, thrower,
+ null, null, null);
+ }
+ }
+ iterator();
+ }
+ iterator();
+ return ret.promise;
+ }
+
+ function disposerSuccess(value) {
+ var inspection = new PromiseInspection();
+ inspection._settledValue = value;
+ inspection._bitField = 268435456;
+ return dispose(this, inspection).thenReturn(value);
+ }
+
+ function disposerFail(reason) {
+ var inspection = new PromiseInspection();
+ inspection._settledValue = reason;
+ inspection._bitField = 134217728;
+ return dispose(this, inspection).thenThrow(reason);
+ }
+
+ function Disposer(data, promise, context) {
+ this._data = data;
+ this._promise = promise;
+ this._context = context;
+ }
+
+ Disposer.prototype.data = function () {
+ return this._data;
+ };
+
+ Disposer.prototype.promise = function () {
+ return this._promise;
+ };
+
+ Disposer.prototype.resource = function () {
+ if (this.promise().isFulfilled()) {
+ return this.promise().value();
+ }
+ return null;
+ };
+
+ Disposer.prototype.tryDispose = function(inspection) {
+ var resource = this.resource();
+ var context = this._context;
+ if (context !== undefined) context._pushContext();
+ var ret = resource !== null
+ ? this.doDispose(resource, inspection) : null;
+ if (context !== undefined) context._popContext();
+ this._promise._unsetDisposable();
+ this._data = null;
+ return ret;
+ };
+
+ Disposer.isDisposer = function (d) {
+ return (d != null &&
+ typeof d.resource === "function" &&
+ typeof d.tryDispose === "function");
+ };
+
+ function FunctionDisposer(fn, promise, context) {
+ this.constructor$(fn, promise, context);
+ }
+ inherits(FunctionDisposer, Disposer);
+
+ FunctionDisposer.prototype.doDispose = function (resource, inspection) {
+ var fn = this.data();
+ return fn.call(resource, resource, inspection);
+ };
+
+ function maybeUnwrapDisposer(value) {
+ if (Disposer.isDisposer(value)) {
+ this.resources[this.index]._setDisposable(value);
+ return value.promise();
+ }
+ return value;
+ }
+
+ Promise.using = function () {
+ var len = arguments.length;
+ if (len < 2) return apiRejection(
+ "you must pass at least 2 arguments to Promise.using");
+ var fn = arguments[len - 1];
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ len--;
+ var resources = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ var resource = arguments[i];
+ if (Disposer.isDisposer(resource)) {
+ var disposer = resource;
+ resource = resource.promise();
+ resource._setDisposable(disposer);
+ } else {
+ var maybePromise = tryConvertToPromise(resource);
+ if (maybePromise instanceof Promise) {
+ resource =
+ maybePromise._then(maybeUnwrapDisposer, null, null, {
+ resources: resources,
+ index: i
+ }, undefined);
+ }
+ }
+ resources[i] = resource;
+ }
+
+ var promise = Promise.settle(resources)
+ .then(inspectionMapper)
+ .then(function(vals) {
+ promise._pushContext();
+ var ret;
+ try {
+ ret = fn.apply(undefined, vals);
+ } finally {
+ promise._popContext();
+ }
+ return ret;
+ })
+ ._then(
+ disposerSuccess, disposerFail, undefined, resources, undefined);
+ resources.promise = promise;
+ return promise;
+ };
+
+ Promise.prototype._setDisposable = function (disposer) {
+ this._bitField = this._bitField | 262144;
+ this._disposer = disposer;
+ };
+
+ Promise.prototype._isDisposable = function () {
+ return (this._bitField & 262144) > 0;
+ };
+
+ Promise.prototype._getDisposer = function () {
+ return this._disposer;
+ };
+
+ Promise.prototype._unsetDisposable = function () {
+ this._bitField = this._bitField & (~262144);
+ this._disposer = undefined;
+ };
+
+ Promise.prototype.disposer = function (fn) {
+ if (typeof fn === "function") {
+ return new FunctionDisposer(fn, this, createContext());
+ }
+ throw new TypeError();
+ };
+
+};
+
+},{"./errors.js":13,"./util.js":38}],38:[function(_dereq_,module,exports){
+"use strict";
+var es5 = _dereq_("./es5.js");
+var canEvaluate = typeof navigator == "undefined";
+var haveGetters = (function(){
+ try {
+ var o = {};
+ es5.defineProperty(o, "f", {
+ get: function () {
+ return 3;
+ }
+ });
+ return o.f === 3;
+ }
+ catch (e) {
+ return false;
+ }
+
+})();
+
+var errorObj = {e: {}};
+var tryCatchTarget;
+function tryCatcher() {
+ try {
+ return tryCatchTarget.apply(this, arguments);
+ } catch (e) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+function tryCatch(fn) {
+ tryCatchTarget = fn;
+ return tryCatcher;
+}
+
+var inherits = function(Child, Parent) {
+ var hasProp = {}.hasOwnProperty;
+
+ function T() {
+ this.constructor = Child;
+ this.constructor$ = Parent;
+ for (var propertyName in Parent.prototype) {
+ if (hasProp.call(Parent.prototype, propertyName) &&
+ propertyName.charAt(propertyName.length-1) !== "$"
+ ) {
+ this[propertyName + "$"] = Parent.prototype[propertyName];
+ }
+ }
+ }
+ T.prototype = Parent.prototype;
+ Child.prototype = new T();
+ return Child.prototype;
+};
+
+
+function isPrimitive(val) {
+ return val == null || val === true || val === false ||
+ typeof val === "string" || typeof val === "number";
+
+}
+
+function isObject(value) {
+ return !isPrimitive(value);
+}
+
+function maybeWrapAsError(maybeError) {
+ if (!isPrimitive(maybeError)) return maybeError;
+
+ return new Error(safeToString(maybeError));
+}
+
+function withAppended(target, appendee) {
+ var len = target.length;
+ var ret = new Array(len + 1);
+ var i;
+ for (i = 0; i < len; ++i) {
+ ret[i] = target[i];
+ }
+ ret[i] = appendee;
+ return ret;
+}
+
+function getDataPropertyOrDefault(obj, key, defaultValue) {
+ if (es5.isES5) {
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+ if (desc != null) {
+ return desc.get == null && desc.set == null
+ ? desc.value
+ : defaultValue;
+ }
+ } else {
+ return {}.hasOwnProperty.call(obj, key) ? obj[key] : undefined;
+ }
+}
+
+function notEnumerableProp(obj, name, value) {
+ if (isPrimitive(obj)) return obj;
+ var descriptor = {
+ value: value,
+ configurable: true,
+ enumerable: false,
+ writable: true
+ };
+ es5.defineProperty(obj, name, descriptor);
+ return obj;
+}
+
+
+var wrapsPrimitiveReceiver = (function() {
+ return this !== "string";
+}).call("string");
+
+function thrower(r) {
+ throw r;
+}
+
+var inheritedDataKeys = (function() {
+ if (es5.isES5) {
+ var oProto = Object.prototype;
+ var getKeys = Object.getOwnPropertyNames;
+ return function(obj) {
+ var ret = [];
+ var visitedKeys = Object.create(null);
+ while (obj != null && obj !== oProto) {
+ var keys;
+ try {
+ keys = getKeys(obj);
+ } catch (e) {
+ return ret;
+ }
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (visitedKeys[key]) continue;
+ visitedKeys[key] = true;
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+ if (desc != null && desc.get == null && desc.set == null) {
+ ret.push(key);
+ }
+ }
+ obj = es5.getPrototypeOf(obj);
+ }
+ return ret;
+ };
+ } else {
+ return function(obj) {
+ var ret = [];
+ /*jshint forin:false */
+ for (var key in obj) {
+ ret.push(key);
+ }
+ return ret;
+ };
+ }
+
+})();
+
+function isClass(fn) {
+ try {
+ if (typeof fn === "function") {
+ var keys = es5.names(fn.prototype);
+ if (es5.isES5) return keys.length > 1;
+ return keys.length > 0 &&
+ !(keys.length === 1 && keys[0] === "constructor");
+ }
+ return false;
+ } catch (e) {
+ return false;
+ }
+}
+
+function toFastProperties(obj) {
+ /*jshint -W027,-W055,-W031*/
+ function f() {}
+ f.prototype = obj;
+ var l = 8;
+ while (l--) new f();
+ return obj;
+ eval(obj);
+}
+
+var rident = /^[a-z$_][a-z$_0-9]*$/i;
+function isIdentifier(str) {
+ return rident.test(str);
+}
+
+function filledRange(count, prefix, suffix) {
+ var ret = new Array(count);
+ for(var i = 0; i < count; ++i) {
+ ret[i] = prefix + i + suffix;
+ }
+ return ret;
+}
+
+function safeToString(obj) {
+ try {
+ return obj + "";
+ } catch (e) {
+ return "[no string representation]";
+ }
+}
+
+function markAsOriginatingFromRejection(e) {
+ try {
+ notEnumerableProp(e, "isOperational", true);
+ }
+ catch(ignore) {}
+}
+
+function originatesFromRejection(e) {
+ if (e == null) return false;
+ return ((e instanceof Error["__BluebirdErrorTypes__"].OperationalError) ||
+ e["isOperational"] === true);
+}
+
+function canAttachTrace(obj) {
+ return obj instanceof Error && es5.propertyIsWritable(obj, "stack");
+}
+
+var ensureErrorObject = (function() {
+ if (!("stack" in new Error())) {
+ return function(value) {
+ if (canAttachTrace(value)) return value;
+ try {throw new Error(safeToString(value));}
+ catch(err) {return err;}
+ };
+ } else {
+ return function(value) {
+ if (canAttachTrace(value)) return value;
+ return new Error(safeToString(value));
+ };
+ }
+})();
+
+function classString(obj) {
+ return {}.toString.call(obj);
+}
+
+function copyDescriptors(from, to, filter) {
+ var keys = es5.names(from);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (filter(key)) {
+ es5.defineProperty(to, key, es5.getDescriptor(from, key));
+ }
+ }
+}
+
+var ret = {
+ isClass: isClass,
+ isIdentifier: isIdentifier,
+ inheritedDataKeys: inheritedDataKeys,
+ getDataPropertyOrDefault: getDataPropertyOrDefault,
+ thrower: thrower,
+ isArray: es5.isArray,
+ haveGetters: haveGetters,
+ notEnumerableProp: notEnumerableProp,
+ isPrimitive: isPrimitive,
+ isObject: isObject,
+ canEvaluate: canEvaluate,
+ errorObj: errorObj,
+ tryCatch: tryCatch,
+ inherits: inherits,
+ withAppended: withAppended,
+ maybeWrapAsError: maybeWrapAsError,
+ wrapsPrimitiveReceiver: wrapsPrimitiveReceiver,
+ toFastProperties: toFastProperties,
+ filledRange: filledRange,
+ toString: safeToString,
+ canAttachTrace: canAttachTrace,
+ ensureErrorObject: ensureErrorObject,
+ originatesFromRejection: originatesFromRejection,
+ markAsOriginatingFromRejection: markAsOriginatingFromRejection,
+ classString: classString,
+ copyDescriptors: copyDescriptors,
+ hasDevTools: typeof chrome !== "undefined" && chrome &&
+ typeof chrome.loadTimes === "function",
+ isNode: typeof process !== "undefined" &&
+ classString(process).toLowerCase() === "[object process]"
+};
+try {throw new Error(); } catch (e) {ret.lastLineError = e;}
+module.exports = ret;
+
+},{"./es5.js":14}],39:[function(_dereq_,module,exports){
+// Copyright Joyent, Inc. and other Node 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.
+
+function EventEmitter() {
+ this._events = this._events || {};
+ this._maxListeners = this._maxListeners || undefined;
+}
+module.exports = EventEmitter;
+
+// Backwards-compat with node 0.10.x
+EventEmitter.EventEmitter = EventEmitter;
+
+EventEmitter.prototype._events = undefined;
+EventEmitter.prototype._maxListeners = undefined;
+
+// By default EventEmitters will print a warning if more than 10 listeners are
+// added to it. This is a useful default which helps finding memory leaks.
+EventEmitter.defaultMaxListeners = 10;
+
+// Obviously not all Emitters should be limited to 10. This function allows
+// that to be increased. Set to zero for unlimited.
+EventEmitter.prototype.setMaxListeners = function(n) {
+ if (!isNumber(n) || n < 0 || isNaN(n))
+ throw TypeError('n must be a positive number');
+ this._maxListeners = n;
+ return this;
+};
+
+EventEmitter.prototype.emit = function(type) {
+ var er, handler, len, args, i, listeners;
+
+ if (!this._events)
+ this._events = {};
+
+ // If there is no 'error' event listener then throw.
+ if (type === 'error') {
+ if (!this._events.error ||
+ (isObject(this._events.error) && !this._events.error.length)) {
+ er = arguments[1];
+ if (er instanceof Error) {
+ throw er; // Unhandled 'error' event
+ }
+ throw TypeError('Uncaught, unspecified "error" event.');
+ }
+ }
+
+ handler = this._events[type];
+
+ if (isUndefined(handler))
+ return false;
+
+ if (isFunction(handler)) {
+ switch (arguments.length) {
+ // fast cases
+ case 1:
+ handler.call(this);
+ break;
+ case 2:
+ handler.call(this, arguments[1]);
+ break;
+ case 3:
+ handler.call(this, arguments[1], arguments[2]);
+ break;
+ // slower
+ default:
+ len = arguments.length;
+ args = new Array(len - 1);
+ for (i = 1; i < len; i++)
+ args[i - 1] = arguments[i];
+ handler.apply(this, args);
+ }
+ } else if (isObject(handler)) {
+ len = arguments.length;
+ args = new Array(len - 1);
+ for (i = 1; i < len; i++)
+ args[i - 1] = arguments[i];
+
+ listeners = handler.slice();
+ len = listeners.length;
+ for (i = 0; i < len; i++)
+ listeners[i].apply(this, args);
+ }
+
+ return true;
+};
+
+EventEmitter.prototype.addListener = function(type, listener) {
+ var m;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events)
+ this._events = {};
+
+ // To avoid recursion in the case that type === "newListener"! Before
+ // adding it to the listeners, first emit "newListener".
+ if (this._events.newListener)
+ this.emit('newListener', type,
+ isFunction(listener.listener) ?
+ listener.listener : listener);
+
+ if (!this._events[type])
+ // Optimize the case of one listener. Don't need the extra array object.
+ this._events[type] = listener;
+ else if (isObject(this._events[type]))
+ // If we've already got an array, just append.
+ this._events[type].push(listener);
+ else
+ // Adding the second element, need to change to array.
+ this._events[type] = [this._events[type], listener];
+
+ // Check for listener leak
+ if (isObject(this._events[type]) && !this._events[type].warned) {
+ var m;
+ if (!isUndefined(this._maxListeners)) {
+ m = this._maxListeners;
+ } else {
+ m = EventEmitter.defaultMaxListeners;
+ }
+
+ if (m && m > 0 && this._events[type].length > m) {
+ this._events[type].warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +
+ 'leak detected. %d listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ this._events[type].length);
+ if (typeof console.trace === 'function') {
+ // not supported in IE 10
+ console.trace();
+ }
+ }
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+
+EventEmitter.prototype.once = function(type, listener) {
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ var fired = false;
+
+ function g() {
+ this.removeListener(type, g);
+
+ if (!fired) {
+ fired = true;
+ listener.apply(this, arguments);
+ }
+ }
+
+ g.listener = listener;
+ this.on(type, g);
+
+ return this;
+};
+
+// emits a 'removeListener' event iff the listener was removed
+EventEmitter.prototype.removeListener = function(type, listener) {
+ var list, position, length, i;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events || !this._events[type])
+ return this;
+
+ list = this._events[type];
+ length = list.length;
+ position = -1;
+
+ if (list === listener ||
+ (isFunction(list.listener) && list.listener === listener)) {
+ delete this._events[type];
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+
+ } else if (isObject(list)) {
+ for (i = length; i-- > 0;) {
+ if (list[i] === listener ||
+ (list[i].listener && list[i].listener === listener)) {
+ position = i;
+ break;
+ }
+ }
+
+ if (position < 0)
+ return this;
+
+ if (list.length === 1) {
+ list.length = 0;
+ delete this._events[type];
+ } else {
+ list.splice(position, 1);
+ }
+
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.removeAllListeners = function(type) {
+ var key, listeners;
+
+ if (!this._events)
+ return this;
+
+ // not listening for removeListener, no need to emit
+ if (!this._events.removeListener) {
+ if (arguments.length === 0)
+ this._events = {};
+ else if (this._events[type])
+ delete this._events[type];
+ return this;
+ }
+
+ // emit removeListener for all listeners on all events
+ if (arguments.length === 0) {
+ for (key in this._events) {
+ if (key === 'removeListener') continue;
+ this.removeAllListeners(key);
+ }
+ this.removeAllListeners('removeListener');
+ this._events = {};
+ return this;
+ }
+
+ listeners = this._events[type];
+
+ if (isFunction(listeners)) {
+ this.removeListener(type, listeners);
+ } else {
+ // LIFO order
+ while (listeners.length)
+ this.removeListener(type, listeners[listeners.length - 1]);
+ }
+ delete this._events[type];
+
+ return this;
+};
+
+EventEmitter.prototype.listeners = function(type) {
+ var ret;
+ if (!this._events || !this._events[type])
+ ret = [];
+ else if (isFunction(this._events[type]))
+ ret = [this._events[type]];
+ else
+ ret = this._events[type].slice();
+ return ret;
+};
+
+EventEmitter.listenerCount = function(emitter, type) {
+ var ret;
+ if (!emitter._events || !emitter._events[type])
+ ret = 0;
+ else if (isFunction(emitter._events[type]))
+ ret = 1;
+ else
+ ret = emitter._events[type].length;
+ return ret;
+};
+
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+
+},{}]},{},[4])(4)
+}); ;if (typeof window !== 'undefined' && window !== null) { window.P = window.Promise; } else if (typeof self !== 'undefined' && self !== null) { self.P = self.Promise; }
\ No newline at end of file
--- /dev/null
+/* @preserve
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Petka Antonov
+ *
+ * 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:</p>
+ *
+ * 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.
+ *
+ */
+/**
+ * bluebird build version 2.9.25
+ * Features enabled: core, race, call_get, generators, map, nodeify, promisify, props, reduce, settle, some, cancel, using, filter, any, each, timers
+*/
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.Promise=t()}}(function(){var t,e,r;return function n(t,e,r){function i(s,a){if(!e[s]){if(!t[s]){var u="function"==typeof _dereq_&&_dereq_;if(!a&&u)return u(s,!0);if(o)return o(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var l=e[s]={exports:{}};t[s][0].call(l.exports,function(e){var r=t[s][1][e];return i(r?r:e)},l,l.exports,n,t,e,r)}return e[s].exports}for(var o="function"==typeof _dereq_&&_dereq_,s=0;s<r.length;s++)i(r[s]);return i}({1:[function(t,e){"use strict";e.exports=function(t){function e(t){var e=new r(t),n=e.promise();return e.setHowMany(1),e.setUnwrap(),e.init(),n}var r=t._SomePromiseArray;t.any=function(t){return e(t)},t.prototype.any=function(){return e(this)}}},{}],2:[function(t,e){"use strict";function r(){this._isTickUsed=!1,this._lateQueue=new c(16),this._normalQueue=new c(16),this._trampolineEnabled=!0;var t=this;this.drainQueues=function(){t._drainQueues()},this._schedule=u.isStatic?u(this.drainQueues):u}function n(t,e,r){var n=this._getDomain();void 0!==n&&(t=n.bind(t)),this._lateQueue.push(t,e,r),this._queueTick()}function i(t,e,r){var n=this._getDomain();void 0!==n&&(t=n.bind(t)),this._normalQueue.push(t,e,r),this._queueTick()}function o(t){var e=this._getDomain();if(void 0!==e){var r=e.bind(t._settlePromises);this._normalQueue.push(r,t,void 0)}else this._normalQueue._pushOne(t);this._queueTick()}var s;try{throw new Error}catch(a){s=a}var u=t("./schedule.js"),c=t("./queue.js"),l=t("./util.js");r.prototype.disableTrampolineIfNecessary=function(){l.hasDevTools&&(this._trampolineEnabled=!1)},r.prototype.enableTrampoline=function(){this._trampolineEnabled||(this._trampolineEnabled=!0,this._schedule=function(t){setTimeout(t,0)})},r.prototype.haveItemsQueued=function(){return this._normalQueue.length()>0},r.prototype.throwLater=function(t,e){1===arguments.length&&(e=t,t=function(){throw e});var r=this._getDomain();if(void 0!==r&&(t=r.bind(t)),"undefined"!=typeof setTimeout)setTimeout(function(){t(e)},0);else try{this._schedule(function(){t(e)})}catch(n){throw new Error("No async scheduler available\n\n See http://goo.gl/m3OTXk\n")}},r.prototype._getDomain=function(){};l.hasDevTools?(r.prototype.invokeLater=function(t,e,r){this._trampolineEnabled?n.call(this,t,e,r):setTimeout(function(){t.call(e,r)},100)},r.prototype.invoke=function(t,e,r){this._trampolineEnabled?i.call(this,t,e,r):setTimeout(function(){t.call(e,r)},0)},r.prototype.settlePromises=function(t){this._trampolineEnabled?o.call(this,t):setTimeout(function(){t._settlePromises()},0)}):(r.prototype.invokeLater=n,r.prototype.invoke=i,r.prototype.settlePromises=o),r.prototype.invokeFirst=function(t,e,r){var n=this._getDomain();void 0!==n&&(t=n.bind(t)),this._normalQueue.unshift(t,e,r),this._queueTick()},r.prototype._drainQueue=function(t){for(;t.length()>0;){var e=t.shift();if("function"==typeof e){var r=t.shift(),n=t.shift();e.call(r,n)}else e._settlePromises()}},r.prototype._drainQueues=function(){this._drainQueue(this._normalQueue),this._reset(),this._drainQueue(this._lateQueue)},r.prototype._queueTick=function(){this._isTickUsed||(this._isTickUsed=!0,this._schedule(this.drainQueues))},r.prototype._reset=function(){this._isTickUsed=!1},e.exports=new r,e.exports.firstLineError=s},{"./queue.js":28,"./schedule.js":31,"./util.js":38,events:39}],3:[function(t,e){"use strict";e.exports=function(t,e,r){var n=function(t,e){this._reject(e)},i=function(t,e){e.promiseRejectionQueued=!0,e.bindingPromise._then(n,n,null,this,t)},o=function(t,e){this._setBoundTo(t),this._isPending()&&this._resolveCallback(e.target)},s=function(t,e){e.promiseRejectionQueued||this._reject(t)};t.prototype.bind=function(n){var a=r(n),u=new t(e);u._propagateFrom(this,1);var c=this._target();if(a instanceof t){var l={promiseRejectionQueued:!1,promise:u,target:c,bindingPromise:a};c._then(e,i,u._progress,u,l),a._then(o,s,u._progress,u,l)}else u._setBoundTo(n),u._resolveCallback(c);return u},t.prototype._setBoundTo=function(t){void 0!==t?(this._bitField=131072|this._bitField,this._boundTo=t):this._bitField=-131073&this._bitField},t.prototype._isBound=function(){return 131072===(131072&this._bitField)},t.bind=function(n,i){var o=r(n),s=new t(e);return o instanceof t?o._then(function(t){s._setBoundTo(t),s._resolveCallback(i)},s._reject,s._progress,s,null):(s._setBoundTo(n),s._resolveCallback(i)),s}}},{}],4:[function(t,e){"use strict";function r(){try{Promise===i&&(Promise=n)}catch(t){}return i}var n;"undefined"!=typeof Promise&&(n=Promise);var i=t("./promise.js")();i.noConflict=r,e.exports=i},{"./promise.js":23}],5:[function(t,e){"use strict";var r=Object.create;if(r){var n=r(null),i=r(null);n[" size"]=i[" size"]=0}e.exports=function(e){function r(t,r){var n;if(null!=t&&(n=t[r]),"function"!=typeof n){var i="Object "+a.classString(t)+" has no method '"+a.toString(r)+"'";throw new e.TypeError(i)}return n}function n(t){var e=this.pop(),n=r(t,e);return n.apply(t,this)}function i(t){return t[this]}function o(t){var e=+this;return 0>e&&(e=Math.max(0,e+t.length)),t[e]}{var s,a=t("./util.js"),u=a.canEvaluate;a.isIdentifier}e.prototype.call=function(t){for(var e=arguments.length,r=new Array(e-1),i=1;e>i;++i)r[i-1]=arguments[i];return r.push(t),this._then(n,void 0,void 0,r,void 0)},e.prototype.get=function(t){var e,r="number"==typeof t;if(r)e=o;else if(u){var n=s(t);e=null!==n?n:i}else e=i;return this._then(e,void 0,void 0,t,void 0)}}},{"./util.js":38}],6:[function(t,e){"use strict";e.exports=function(e){var r=t("./errors.js"),n=t("./async.js"),i=r.CancellationError;e.prototype._cancel=function(t){if(!this.isCancellable())return this;for(var e,r=this;void 0!==(e=r._cancellationParent)&&e.isCancellable();)r=e;this._unsetCancellable(),r._target()._rejectCallback(t,!1,!0)},e.prototype.cancel=function(t){return this.isCancellable()?(void 0===t&&(t=new i),n.invokeLater(this._cancel,this,t),this):this},e.prototype.cancellable=function(){return this._cancellable()?this:(n.enableTrampoline(),this._setCancellable(),this._cancellationParent=void 0,this)},e.prototype.uncancellable=function(){var t=this.then();return t._unsetCancellable(),t},e.prototype.fork=function(t,e,r){var n=this._then(t,e,r,void 0,void 0);return n._setCancellable(),n._cancellationParent=void 0,n}}},{"./async.js":2,"./errors.js":13}],7:[function(t,e){"use strict";e.exports=function(){function e(t){this._parent=t;var r=this._length=1+(void 0===t?0:t._length);j(this,e),r>32&&this.uncycle()}function r(t,e){for(var r=0;r<e.length-1;++r)e[r].push("From previous event:"),e[r]=e[r].join("\n");return r<e.length&&(e[r]=e[r].join("\n")),t+"\n"+e.join("\n")}function n(t){for(var e=0;e<t.length;++e)(0===t[e].length||e+1<t.length&&t[e][0]===t[e+1][0])&&(t.splice(e,1),e--)}function i(t){for(var e=t[0],r=1;r<t.length;++r){for(var n=t[r],i=e.length-1,o=e[i],s=-1,a=n.length-1;a>=0;--a)if(n[a]===o){s=a;break}for(var a=s;a>=0;--a){var u=n[a];if(e[i]!==u)break;e.pop(),i--}e=n}}function o(t){for(var e=[],r=0;r<t.length;++r){var n=t[r],i=_.test(n)||" (No stack trace)"===n,o=i&&y(n);i&&!o&&(v&&" "!==n.charAt(0)&&(n=" "+n),e.push(n))}return e}function s(t){for(var e=t.stack.replace(/\s+$/g,"").split("\n"),r=0;r<e.length;++r){var n=e[r];if(" (No stack trace)"===n||_.test(n))break}return r>0&&(e=e.slice(r)),e}function a(t){var e;if("function"==typeof t)e="[function "+(t.name||"anonymous")+"]";else{e=t.toString();var r=/\[object [a-zA-Z0-9$_]+\]/;if(r.test(e))try{var n=JSON.stringify(t);e=n}catch(i){}0===e.length&&(e="(empty array)")}return"(<"+u(e)+">, no stack trace)"}function u(t){var e=41;return t.length<e?t:t.substr(0,e-3)+"..."}function c(t){var e=t.match(g);return e?{fileName:e[1],line:parseInt(e[2],10)}:void 0}var l,h=t("./async.js"),p=t("./util.js"),f=/[\\\/]bluebird[\\\/]js[\\\/](main|debug|zalgo|instrumented)/,_=null,d=null,v=!1;p.inherits(e,Error),e.prototype.uncycle=function(){var t=this._length;if(!(2>t)){for(var e=[],r={},n=0,i=this;void 0!==i;++n)e.push(i),i=i._parent;t=this._length=n;for(var n=t-1;n>=0;--n){var o=e[n].stack;void 0===r[o]&&(r[o]=n)}for(var n=0;t>n;++n){var s=e[n].stack,a=r[s];if(void 0!==a&&a!==n){a>0&&(e[a-1]._parent=void 0,e[a-1]._length=1),e[n]._parent=void 0,e[n]._length=1;var u=n>0?e[n-1]:this;t-1>a?(u._parent=e[a+1],u._parent.uncycle(),u._length=u._parent._length+1):(u._parent=void 0,u._length=1);for(var c=u._length+1,l=n-2;l>=0;--l)e[l]._length=c,c++;return}}}},e.prototype.parent=function(){return this._parent},e.prototype.hasParent=function(){return void 0!==this._parent},e.prototype.attachExtraTrace=function(t){if(!t.__stackCleaned__){this.uncycle();for(var s=e.parseStackAndMessage(t),a=s.message,u=[s.stack],c=this;void 0!==c;)u.push(o(c.stack.split("\n"))),c=c._parent;i(u),n(u),p.notEnumerableProp(t,"stack",r(a,u)),p.notEnumerableProp(t,"__stackCleaned__",!0)}},e.parseStackAndMessage=function(t){var e=t.stack,r=t.toString();return e="string"==typeof e&&e.length>0?s(t):[" (No stack trace)"],{message:r,stack:o(e)}},e.formatAndLogError=function(t,e){if("undefined"!=typeof console){var r;if("object"==typeof t||"function"==typeof t){var n=t.stack;r=e+d(n,t)}else r=e+String(t);"function"==typeof l?l(r):("function"==typeof console.log||"object"==typeof console.log)&&console.log(r)}},e.unhandledRejection=function(t){e.formatAndLogError(t,"^--- With additional stack trace: ")},e.isSupported=function(){return"function"==typeof j},e.fireRejectionEvent=function(t,r,n,i){var o=!1;try{"function"==typeof r&&(o=!0,"rejectionHandled"===t?r(i):r(n,i))}catch(s){h.throwLater(s)}var a=!1;try{a=b(t,n,i)}catch(s){a=!0,h.throwLater(s)}var u=!1;if(m)try{u=m(t.toLowerCase(),{reason:n,promise:i})}catch(s){u=!0,h.throwLater(s)}a||o||u||"unhandledRejection"!==t||e.formatAndLogError(n,"Unhandled rejection ")};var y=function(){return!1},g=/[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;e.setBounds=function(t,r){if(e.isSupported()){for(var n,i,o=t.stack.split("\n"),s=r.stack.split("\n"),a=-1,u=-1,l=0;l<o.length;++l){var h=c(o[l]);if(h){n=h.fileName,a=h.line;break}}for(var l=0;l<s.length;++l){var h=c(s[l]);if(h){i=h.fileName,u=h.line;break}}0>a||0>u||!n||!i||n!==i||a>=u||(y=function(t){if(f.test(t))return!0;var e=c(t);return e&&e.fileName===n&&a<=e.line&&e.line<=u?!0:!1})}};var m,j=function(){var t=/^\s*at\s*/,e=function(t,e){return"string"==typeof t?t:void 0!==e.name&&void 0!==e.message?e.toString():a(e)};if("number"==typeof Error.stackTraceLimit&&"function"==typeof Error.captureStackTrace){Error.stackTraceLimit=Error.stackTraceLimit+6,_=t,d=e;var r=Error.captureStackTrace;return y=function(t){return f.test(t)},function(t,e){Error.stackTraceLimit=Error.stackTraceLimit+6,r(t,e),Error.stackTraceLimit=Error.stackTraceLimit-6}}var n=new Error;if("string"==typeof n.stack&&n.stack.split("\n")[0].indexOf("stackDetection@")>=0)return _=/@/,d=e,v=!0,function(t){t.stack=(new Error).stack};var i;try{throw new Error}catch(o){i="stack"in o}return"stack"in n||!i?(d=function(t,e){return"string"==typeof t?t:"object"!=typeof e&&"function"!=typeof e||void 0===e.name||void 0===e.message?a(e):e.toString()},null):(_=t,d=e,function(t){Error.stackTraceLimit=Error.stackTraceLimit+6;try{throw new Error}catch(e){t.stack=e.stack}Error.stackTraceLimit=Error.stackTraceLimit-6})}([]),b=function(){if(p.isNode)return function(t,e,r){return"rejectionHandled"===t?process.emit(t,r):process.emit(t,e,r)};var t=!1,e=!0;try{var r=new self.CustomEvent("test");t=r instanceof CustomEvent}catch(n){}if(!t)try{var i=document.createEvent("CustomEvent");i.initCustomEvent("testingtheevent",!1,!0,{}),self.dispatchEvent(i)}catch(n){e=!1}e&&(m=function(e,r){var n;return t?n=new self.CustomEvent(e,{detail:r,bubbles:!1,cancelable:!0}):self.dispatchEvent&&(n=document.createEvent("CustomEvent"),n.initCustomEvent(e,!1,!0,r)),n?!self.dispatchEvent(n):!1});var o={};return o.unhandledRejection="onunhandledRejection".toLowerCase(),o.rejectionHandled="onrejectionHandled".toLowerCase(),function(t,e,r){var n=o[t],i=self[n];return i?("rejectionHandled"===t?i.call(self,r):i.call(self,e,r),!0):!1}}();return"undefined"!=typeof console&&"undefined"!=typeof console.warn&&(l=function(t){console.warn(t)},p.isNode&&process.stderr.isTTY?l=function(t){process.stderr.write("\e[31m"+t+"\e[39m\n")}:p.isNode||"string"!=typeof(new Error).stack||(l=function(t){console.warn("%c"+t,"color: red")})),e}},{"./async.js":2,"./util.js":38}],8:[function(t,e){"use strict";e.exports=function(e){function r(t,e,r){this._instances=t,this._callback=e,this._promise=r}function n(t,e){var r={},n=s(t).call(r,e);if(n===a)return n;var i=u(r);return i.length?(a.e=new c("Catch filter must inherit from Error or be a simple predicate function\n\n See http://goo.gl/o84o68\n"),a):n}var i=t("./util.js"),o=t("./errors.js"),s=i.tryCatch,a=i.errorObj,u=t("./es5.js").keys,c=o.TypeError;return r.prototype.doFilter=function(t){for(var r=this._callback,i=this._promise,o=i._boundTo,u=0,c=this._instances.length;c>u;++u){var l=this._instances[u],h=l===Error||null!=l&&l.prototype instanceof Error;if(h&&t instanceof l){var p=s(r).call(o,t);return p===a?(e.e=p.e,e):p}if("function"==typeof l&&!h){var f=n(l,t);if(f===a){t=a.e;break}if(f){var p=s(r).call(o,t);return p===a?(e.e=p.e,e):p}}}return e.e=t,e},r}},{"./errors.js":13,"./es5.js":14,"./util.js":38}],9:[function(t,e){"use strict";e.exports=function(t,e,r){function n(){this._trace=new e(o())}function i(){return r()?new n:void 0}function o(){var t=s.length-1;return t>=0?s[t]:void 0}var s=[];return n.prototype._pushContext=function(){r()&&void 0!==this._trace&&s.push(this._trace)},n.prototype._popContext=function(){r()&&void 0!==this._trace&&s.pop()},t.prototype._peekContext=o,t.prototype._pushContext=n.prototype._pushContext,t.prototype._popContext=n.prototype._popContext,i}},{}],10:[function(t,e){"use strict";e.exports=function(e,r){var n,i,o=t("./async.js"),s=t("./errors.js").Warning,a=t("./util.js"),u=a.canAttachTrace,c=!1||a.isNode&&(!!process.env.BLUEBIRD_DEBUG||"development"===process.env.NODE_ENV);return c&&o.disableTrampolineIfNecessary(),e.prototype._ensurePossibleRejectionHandled=function(){this._setRejectionIsUnhandled(),o.invokeLater(this._notifyUnhandledRejection,this,void 0)},e.prototype._notifyUnhandledRejectionIsHandled=function(){r.fireRejectionEvent("rejectionHandled",n,void 0,this)},e.prototype._notifyUnhandledRejection=function(){if(this._isRejectionUnhandled()){var t=this._getCarriedStackTrace()||this._settledValue;this._setUnhandledRejectionIsNotified(),r.fireRejectionEvent("unhandledRejection",i,t,this)}},e.prototype._setUnhandledRejectionIsNotified=function(){this._bitField=524288|this._bitField},e.prototype._unsetUnhandledRejectionIsNotified=function(){this._bitField=-524289&this._bitField},e.prototype._isUnhandledRejectionNotified=function(){return(524288&this._bitField)>0},e.prototype._setRejectionIsUnhandled=function(){this._bitField=2097152|this._bitField},e.prototype._unsetRejectionIsUnhandled=function(){this._bitField=-2097153&this._bitField,this._isUnhandledRejectionNotified()&&(this._unsetUnhandledRejectionIsNotified(),this._notifyUnhandledRejectionIsHandled())},e.prototype._isRejectionUnhandled=function(){return(2097152&this._bitField)>0},e.prototype._setCarriedStackTrace=function(t){this._bitField=1048576|this._bitField,this._fulfillmentHandler0=t},e.prototype._isCarryingStackTrace=function(){return(1048576&this._bitField)>0},e.prototype._getCarriedStackTrace=function(){return this._isCarryingStackTrace()?this._fulfillmentHandler0:void 0},e.prototype._captureStackTrace=function(){return c&&(this._trace=new r(this._peekContext())),this},e.prototype._attachExtraTrace=function(t,e){if(c&&u(t)){var n=this._trace;if(void 0!==n&&e&&(n=n._parent),void 0!==n)n.attachExtraTrace(t);else if(!t.__stackCleaned__){var i=r.parseStackAndMessage(t);a.notEnumerableProp(t,"stack",i.message+"\n"+i.stack.join("\n")),a.notEnumerableProp(t,"__stackCleaned__",!0)}}},e.prototype._warn=function(t){var e=new s(t),n=this._peekContext();if(n)n.attachExtraTrace(e);else{var i=r.parseStackAndMessage(e);e.stack=i.message+"\n"+i.stack.join("\n")}r.formatAndLogError(e,"")},e.onPossiblyUnhandledRejection=function(t){i="function"==typeof t?t:void 0},e.onUnhandledRejectionHandled=function(t){n="function"==typeof t?t:void 0},e.longStackTraces=function(){if(o.haveItemsQueued()&&c===!1)throw new Error("cannot enable long stack traces after promises have been created\n\n See http://goo.gl/DT1qyG\n");c=r.isSupported(),c&&o.disableTrampolineIfNecessary()},e.hasLongStackTraces=function(){return c&&r.isSupported()},r.isSupported()||(e.longStackTraces=function(){},c=!1),function(){return c}}},{"./async.js":2,"./errors.js":13,"./util.js":38}],11:[function(t,e){"use strict";var r=t("./util.js"),n=r.isPrimitive,i=r.wrapsPrimitiveReceiver;e.exports=function(t){var e=function(){return this},r=function(){throw this},o=function(t,e){return 1===e?function(){throw t}:2===e?function(){return t}:void 0};t.prototype["return"]=t.prototype.thenReturn=function(t){return i&&n(t)?this._then(o(t,2),void 0,void 0,void 0,void 0):this._then(e,void 0,void 0,t,void 0)},t.prototype["throw"]=t.prototype.thenThrow=function(t){return i&&n(t)?this._then(o(t,1),void 0,void 0,void 0,void 0):this._then(r,void 0,void 0,t,void 0)}}},{"./util.js":38}],12:[function(t,e){"use strict";e.exports=function(t,e){var r=t.reduce;t.prototype.each=function(t){return r(this,t,null,e)},t.each=function(t,n){return r(t,n,null,e)}}},{}],13:[function(t,e){"use strict";function r(t,e){function r(n){return this instanceof r?(l(this,"message","string"==typeof n?n:e),l(this,"name",t),void(Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):Error.call(this))):new r(n)}return c(r,Error),r}function n(t){return this instanceof n?(l(this,"name","OperationalError"),l(this,"message",t),this.cause=t,this.isOperational=!0,void(t instanceof Error?(l(this,"message",t.message),l(this,"stack",t.stack)):Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor))):new n(t)}var i,o,s=t("./es5.js"),a=s.freeze,u=t("./util.js"),c=u.inherits,l=u.notEnumerableProp,h=r("Warning","warning"),p=r("CancellationError","cancellation error"),f=r("TimeoutError","timeout error"),_=r("AggregateError","aggregate error");try{i=TypeError,o=RangeError}catch(d){i=r("TypeError","type error"),o=r("RangeError","range error")}for(var v="join pop push shift unshift slice filter forEach some every map indexOf lastIndexOf reduce reduceRight sort reverse".split(" "),y=0;y<v.length;++y)"function"==typeof Array.prototype[v[y]]&&(_.prototype[v[y]]=Array.prototype[v[y]]);s.defineProperty(_.prototype,"length",{value:0,configurable:!1,writable:!0,enumerable:!0}),_.prototype.isOperational=!0;var g=0;_.prototype.toString=function(){var t=Array(4*g+1).join(" "),e="\n"+t+"AggregateError of:\n";g++,t=Array(4*g+1).join(" ");for(var r=0;r<this.length;++r){for(var n=this[r]===this?"[Circular AggregateError]":this[r]+"",i=n.split("\n"),o=0;o<i.length;++o)i[o]=t+i[o];n=i.join("\n"),e+=n+"\n"}return g--,e},c(n,Error);var m=Error.__BluebirdErrorTypes__;m||(m=a({CancellationError:p,TimeoutError:f,OperationalError:n,RejectionError:n,AggregateError:_}),l(Error,"__BluebirdErrorTypes__",m)),e.exports={Error:Error,TypeError:i,RangeError:o,CancellationError:m.CancellationError,OperationalError:m.OperationalError,TimeoutError:m.TimeoutError,AggregateError:m.AggregateError,Warning:h}},{"./es5.js":14,"./util.js":38}],14:[function(t,e){var r=function(){"use strict";return void 0===this}();if(r)e.exports={freeze:Object.freeze,defineProperty:Object.defineProperty,getDescriptor:Object.getOwnPropertyDescriptor,keys:Object.keys,names:Object.getOwnPropertyNames,getPrototypeOf:Object.getPrototypeOf,isArray:Array.isArray,isES5:r,propertyIsWritable:function(t,e){var r=Object.getOwnPropertyDescriptor(t,e);return!(r&&!r.writable&&!r.set)}};else{var n={}.hasOwnProperty,i={}.toString,o={}.constructor.prototype,s=function(t){var e=[];for(var r in t)n.call(t,r)&&e.push(r);return e},a=function(t,e){return{value:t[e]}},u=function(t,e,r){return t[e]=r.value,t},c=function(t){return t},l=function(t){try{return Object(t).constructor.prototype}catch(e){return o}},h=function(t){try{return"[object Array]"===i.call(t)}catch(e){return!1}};e.exports={isArray:h,keys:s,names:s,defineProperty:u,getDescriptor:a,freeze:c,getPrototypeOf:l,isES5:r,propertyIsWritable:function(){return!0}}}},{}],15:[function(t,e){"use strict";e.exports=function(t,e){var r=t.map;t.prototype.filter=function(t,n){return r(this,t,n,e)},t.filter=function(t,n,i){return r(t,n,i,e)}}},{}],16:[function(t,e){"use strict";e.exports=function(e,r,n){function i(){return this}function o(){throw this}function s(t){return function(){return t}}function a(t){return function(){throw t}}function u(t,e,r){var n;return n=p&&f(e)?r?s(e):a(e):r?i:o,t._then(n,_,void 0,e,void 0)}function c(t){var i=this.promise,o=this.handler,s=i._isBound()?o.call(i._boundTo):o();if(void 0!==s){var a=n(s,i);if(a instanceof e)return a=a._target(),u(a,t,i.isFulfilled())}return i.isRejected()?(r.e=t,r):t}function l(t){var r=this.promise,i=this.handler,o=r._isBound()?i.call(r._boundTo,t):i(t);if(void 0!==o){var s=n(o,r);if(s instanceof e)return s=s._target(),u(s,t,!0)}return t}var h=t("./util.js"),p=h.wrapsPrimitiveReceiver,f=h.isPrimitive,_=h.thrower;e.prototype._passThroughHandler=function(t,e){if("function"!=typeof t)return this.then();var r={promise:this,handler:t};return this._then(e?c:l,e?c:void 0,void 0,r,void 0)},e.prototype.lastly=e.prototype["finally"]=function(t){return this._passThroughHandler(t,!0)},e.prototype.tap=function(t){return this._passThroughHandler(t,!1)}}},{"./util.js":38}],17:[function(t,e){"use strict";e.exports=function(e,r,n,i){function o(t,r,n){for(var o=0;o<r.length;++o){n._pushContext();var s=h(r[o])(t);if(n._popContext(),s===l){n._pushContext();var a=e.reject(l.e);return n._popContext(),a}var u=i(s,n);if(u instanceof e)return u}return null}function s(t,r,i,o){var s=this._promise=new e(n);s._captureStackTrace(),this._stack=o,this._generatorFunction=t,this._receiver=r,this._generator=void 0,this._yieldHandlers="function"==typeof i?[i].concat(p):p}var a=t("./errors.js"),u=a.TypeError,c=t("./util.js"),l=c.errorObj,h=c.tryCatch,p=[];s.prototype.promise=function(){return this._promise},s.prototype._run=function(){this._generator=this._generatorFunction.call(this._receiver),this._receiver=this._generatorFunction=void 0,this._next(void 0)},s.prototype._continue=function(t){if(t===l)return this._promise._rejectCallback(t.e,!1,!0);var r=t.value;if(t.done===!0)this._promise._resolveCallback(r);else{var n=i(r,this._promise);if(!(n instanceof e)&&(n=o(n,this._yieldHandlers,this._promise),null===n))return void this._throw(new u("A value %s was yielded that could not be treated as a promise\n\n See http://goo.gl/4Y4pDk\n\n".replace("%s",r)+"From coroutine:\n"+this._stack.split("\n").slice(1,-7).join("\n")));n._then(this._next,this._throw,void 0,this,null)}},s.prototype._throw=function(t){this._promise._attachExtraTrace(t),this._promise._pushContext();var e=h(this._generator["throw"]).call(this._generator,t);this._promise._popContext(),this._continue(e)},s.prototype._next=function(t){this._promise._pushContext();var e=h(this._generator.next).call(this._generator,t);this._promise._popContext(),this._continue(e)},e.coroutine=function(t,e){if("function"!=typeof t)throw new u("generatorFunction must be a function\n\n See http://goo.gl/6Vqhm0\n");var r=Object(e).yieldHandler,n=s,i=(new Error).stack;return function(){var e=t.apply(this,arguments),o=new n(void 0,void 0,r,i);return o._generator=e,o._next(void 0),o.promise()}},e.coroutine.addYieldHandler=function(t){if("function"!=typeof t)throw new u("fn must be a function\n\n See http://goo.gl/916lJJ\n");p.push(t)},e.spawn=function(t){if("function"!=typeof t)return r("generatorFunction must be a function\n\n See http://goo.gl/6Vqhm0\n");var n=new s(t,this),i=n.promise();return n._run(e.spawn),i}}},{"./errors.js":13,"./util.js":38}],18:[function(t,e){"use strict";e.exports=function(e,r,n,i){{var o=t("./util.js");o.canEvaluate,o.tryCatch,o.errorObj}e.join=function(){var t,e=arguments.length-1;if(e>0&&"function"==typeof arguments[e]){t=arguments[e];var n}for(var i=arguments.length,o=new Array(i),s=0;i>s;++s)o[s]=arguments[s];t&&o.pop();var n=new r(o).promise();return void 0!==t?n.spread(t):n}}},{"./util.js":38}],19:[function(t,e){"use strict";e.exports=function(e,r,n,i,o){function s(t,e,r,n){this.constructor$(t),this._promise._captureStackTrace(),this._callback=e,this._preservedValues=n===o?new Array(this.length()):null,this._limit=r,this._inFlight=0,this._queue=r>=1?[]:_,c.invoke(a,this,void 0)}function a(){this._init$(void 0,-2)}function u(t,e,r,n){var i="object"==typeof r&&null!==r?r.concurrency:0;return i="number"==typeof i&&isFinite(i)&&i>=1?i:0,new s(t,e,i,n)}var c=t("./async.js"),l=t("./util.js"),h=l.tryCatch,p=l.errorObj,f={},_=[];l.inherits(s,r),s.prototype._init=function(){},s.prototype._promiseFulfilled=function(t,r){var n=this._values,o=this.length(),s=this._preservedValues,a=this._limit;if(n[r]===f){if(n[r]=t,a>=1&&(this._inFlight--,this._drainQueue(),this._isResolved()))return}else{if(a>=1&&this._inFlight>=a)return n[r]=t,void this._queue.push(r);null!==s&&(s[r]=t);var u=this._callback,c=this._promise._boundTo;this._promise._pushContext();var l=h(u).call(c,t,r,o);if(this._promise._popContext(),l===p)return this._reject(l.e);var _=i(l,this._promise);if(_ instanceof e){if(_=_._target(),_._isPending())return a>=1&&this._inFlight++,n[r]=f,_._proxyPromiseArray(this,r);if(!_._isFulfilled())return this._reject(_._reason());l=_._value()}n[r]=l}var d=++this._totalResolved;d>=o&&(null!==s?this._filter(n,s):this._resolve(n))},s.prototype._drainQueue=function(){for(var t=this._queue,e=this._limit,r=this._values;t.length>0&&this._inFlight<e;){if(this._isResolved())return;var n=t.pop();this._promiseFulfilled(r[n],n)}},s.prototype._filter=function(t,e){for(var r=e.length,n=new Array(r),i=0,o=0;r>o;++o)t[o]&&(n[i++]=e[o]);n.length=i,this._resolve(n)},s.prototype.preservedValues=function(){return this._preservedValues},e.prototype.map=function(t,e){return"function"!=typeof t?n("fn must be a function\n\n See http://goo.gl/916lJJ\n"):u(this,t,e,null).promise()},e.map=function(t,e,r,i){return"function"!=typeof e?n("fn must be a function\n\n See http://goo.gl/916lJJ\n"):u(t,e,r,i).promise()}}},{"./async.js":2,"./util.js":38}],20:[function(t,e){"use strict";e.exports=function(e,r,n,i){var o=t("./util.js"),s=o.tryCatch;e.method=function(t){if("function"!=typeof t)throw new e.TypeError("fn must be a function\n\n See http://goo.gl/916lJJ\n");return function(){var n=new e(r);n._captureStackTrace(),n._pushContext();var i=s(t).apply(this,arguments);return n._popContext(),n._resolveFromSyncValue(i),n}},e.attempt=e["try"]=function(t,n,a){if("function"!=typeof t)return i("fn must be a function\n\n See http://goo.gl/916lJJ\n");var u=new e(r);u._captureStackTrace(),u._pushContext();var c=o.isArray(n)?s(t).apply(a,n):s(t).call(a,n);return u._popContext(),u._resolveFromSyncValue(c),u},e.prototype._resolveFromSyncValue=function(t){t===o.errorObj?this._rejectCallback(t.e,!1,!0):this._resolveCallback(t,!0)}}},{"./util.js":38}],21:[function(t,e){"use strict";e.exports=function(e){function r(t,e){var r=this;if(!o.isArray(t))return n.call(r,t,e);var i=a(e).apply(r._boundTo,[null].concat(t));i===u&&s.throwLater(i.e)}function n(t,e){var r=this,n=r._boundTo,i=void 0===t?a(e).call(n,null):a(e).call(n,null,t);i===u&&s.throwLater(i.e)}function i(t,e){var r=this;if(!t){var n=r._target(),i=n._getCarriedStackTrace();i.cause=t,t=i}var o=a(e).call(r._boundTo,t);o===u&&s.throwLater(o.e)}var o=t("./util.js"),s=t("./async.js"),a=o.tryCatch,u=o.errorObj;e.prototype.asCallback=e.prototype.nodeify=function(t,e){if("function"==typeof t){var o=n;void 0!==e&&Object(e).spread&&(o=r),this._then(o,i,void 0,this,t)}return this}}},{"./async.js":2,"./util.js":38}],22:[function(t,e){"use strict";e.exports=function(e,r){var n=t("./util.js"),i=t("./async.js"),o=n.tryCatch,s=n.errorObj;e.prototype.progressed=function(t){return this._then(void 0,void 0,t,void 0,void 0)},e.prototype._progress=function(t){this._isFollowingOrFulfilledOrRejected()||this._target()._progressUnchecked(t)},e.prototype._progressHandlerAt=function(t){return 0===t?this._progressHandler0:this[(t<<2)+t-5+2]},e.prototype._doProgressWith=function(t){var r=t.value,i=t.handler,a=t.promise,u=t.receiver,c=o(i).call(u,r);if(c===s){if(null!=c.e&&"StopProgressPropagation"!==c.e.name){var l=n.canAttachTrace(c.e)?c.e:new Error(n.toString(c.e));a._attachExtraTrace(l),a._progress(c.e)}}else c instanceof e?c._then(a._progress,null,null,a,void 0):a._progress(c)},e.prototype._progressUnchecked=function(t){for(var n=this._length(),o=this._progress,s=0;n>s;s++){var a=this._progressHandlerAt(s),u=this._promiseAt(s);if(u instanceof e)"function"==typeof a?i.invoke(this._doProgressWith,this,{handler:a,promise:u,receiver:this._receiverAt(s),value:t}):i.invoke(o,u,t);else{var c=this._receiverAt(s);"function"==typeof a?a.call(c,t,u):c instanceof r&&!c._isResolved()&&c._promiseProgressed(t,u)}}}}},{"./async.js":2,"./util.js":38}],23:[function(t,e){"use strict";e.exports=function(){function e(t){if("function"!=typeof t)throw new c("the promise constructor requires a resolver function\n\n See http://goo.gl/EC22Yn\n");if(this.constructor!==e)throw new c("the promise constructor cannot be invoked directly\n\n See http://goo.gl/KsIlge\n");this._bitField=0,this._fulfillmentHandler0=void 0,this._rejectionHandler0=void 0,this._progressHandler0=void 0,this._promise0=void 0,this._receiver0=void 0,this._settledValue=void 0,t!==l&&this._resolveFromResolver(t)}function r(t){var r=new e(l);r._fulfillmentHandler0=t,r._rejectionHandler0=t,r._progressHandler0=t,r._promise0=t,r._receiver0=t,r._settledValue=t}var n=function(){return new c("circular promise resolution chain\n\n See http://goo.gl/LhFpo0\n")},i=function(){return new e.PromiseInspection(this._target())},o=function(t){return e.reject(new c(t))},s=t("./util.js"),a=t("./async.js"),u=t("./errors.js"),c=e.TypeError=u.TypeError;e.RangeError=u.RangeError,e.CancellationError=u.CancellationError,e.TimeoutError=u.TimeoutError,e.OperationalError=u.OperationalError,e.RejectionError=u.OperationalError,e.AggregateError=u.AggregateError;var l=function(){},h={},p={e:null},f=t("./thenables.js")(e,l),_=t("./promise_array.js")(e,l,f,o),d=t("./captured_trace.js")(),v=t("./debuggability.js")(e,d),y=t("./context.js")(e,d,v),g=t("./catch_filter.js")(p),m=t("./promise_resolver.js"),j=m._nodebackForPromise,b=s.errorObj,w=s.tryCatch;return e.prototype.toString=function(){return"[object Promise]"},e.prototype.caught=e.prototype["catch"]=function(t){var r=arguments.length;if(r>1){var n,i=new Array(r-1),o=0;for(n=0;r-1>n;++n){var s=arguments[n];if("function"!=typeof s)return e.reject(new c("Catch filter must inherit from Error or be a simple predicate function\n\n See http://goo.gl/o84o68\n"));i[o++]=s}i.length=o,t=arguments[n];var a=new g(i,t,this);return this._then(void 0,a.doFilter,void 0,a,void 0)}return this._then(void 0,t,void 0,void 0,void 0)},e.prototype.reflect=function(){return this._then(i,i,void 0,this,void 0)},e.prototype.then=function(t,e,r){if(v()&&arguments.length>0&&"function"!=typeof t&&"function"!=typeof e){var n=".then() only accepts functions but was passed: "+s.classString(t);arguments.length>1&&(n+=", "+s.classString(e)),this._warn(n)}return this._then(t,e,r,void 0,void 0)},e.prototype.done=function(t,e,r){var n=this._then(t,e,r,void 0,void 0);n._setIsFinal()},e.prototype.spread=function(t,e){return this.all()._then(t,e,void 0,h,void 0)},e.prototype.isCancellable=function(){return!this.isResolved()&&this._cancellable()},e.prototype.toJSON=function(){var t={isFulfilled:!1,isRejected:!1,fulfillmentValue:void 0,rejectionReason:void 0};
+return this.isFulfilled()?(t.fulfillmentValue=this.value(),t.isFulfilled=!0):this.isRejected()&&(t.rejectionReason=this.reason(),t.isRejected=!0),t},e.prototype.all=function(){return new _(this).promise()},e.prototype.error=function(t){return this.caught(s.originatesFromRejection,t)},e.is=function(t){return t instanceof e},e.fromNode=function(t){var r=new e(l),n=w(t)(j(r));return n===b&&r._rejectCallback(n.e,!0,!0),r},e.all=function(t){return new _(t).promise()},e.defer=e.pending=function(){var t=new e(l);return new m(t)},e.cast=function(t){var r=f(t);if(!(r instanceof e)){var n=r;r=new e(l),r._fulfillUnchecked(n)}return r},e.resolve=e.fulfilled=e.cast,e.reject=e.rejected=function(t){var r=new e(l);return r._captureStackTrace(),r._rejectCallback(t,!0),r},e.setScheduler=function(t){if("function"!=typeof t)throw new c("fn must be a function\n\n See http://goo.gl/916lJJ\n");var e=a._schedule;return a._schedule=t,e},e.prototype._then=function(t,r,n,i,o){var s=void 0!==o,u=s?o:new e(l);s||(u._propagateFrom(this,5),u._captureStackTrace());var c=this._target();c!==this&&(void 0===i&&(i=this._boundTo),s||u._setIsMigrated());var h=c._addCallbacks(t,r,n,u,i);return c._isResolved()&&!c._isSettlePromisesQueued()&&a.invoke(c._settlePromiseAtPostResolution,c,h),u},e.prototype._settlePromiseAtPostResolution=function(t){this._isRejectionUnhandled()&&this._unsetRejectionIsUnhandled(),this._settlePromiseAt(t)},e.prototype._length=function(){return 131071&this._bitField},e.prototype._isFollowingOrFulfilledOrRejected=function(){return(939524096&this._bitField)>0},e.prototype._isFollowing=function(){return 536870912===(536870912&this._bitField)},e.prototype._setLength=function(t){this._bitField=-131072&this._bitField|131071&t},e.prototype._setFulfilled=function(){this._bitField=268435456|this._bitField},e.prototype._setRejected=function(){this._bitField=134217728|this._bitField},e.prototype._setFollowing=function(){this._bitField=536870912|this._bitField},e.prototype._setIsFinal=function(){this._bitField=33554432|this._bitField},e.prototype._isFinal=function(){return(33554432&this._bitField)>0},e.prototype._cancellable=function(){return(67108864&this._bitField)>0},e.prototype._setCancellable=function(){this._bitField=67108864|this._bitField},e.prototype._unsetCancellable=function(){this._bitField=-67108865&this._bitField},e.prototype._setIsMigrated=function(){this._bitField=4194304|this._bitField},e.prototype._unsetIsMigrated=function(){this._bitField=-4194305&this._bitField},e.prototype._isMigrated=function(){return(4194304&this._bitField)>0},e.prototype._receiverAt=function(t){var e=0===t?this._receiver0:this[5*t-5+4];return void 0===e&&this._isBound()?this._boundTo:e},e.prototype._promiseAt=function(t){return 0===t?this._promise0:this[5*t-5+3]},e.prototype._fulfillmentHandlerAt=function(t){return 0===t?this._fulfillmentHandler0:this[5*t-5+0]},e.prototype._rejectionHandlerAt=function(t){return 0===t?this._rejectionHandler0:this[5*t-5+1]},e.prototype._migrateCallbacks=function(t,r){var n=t._fulfillmentHandlerAt(r),i=t._rejectionHandlerAt(r),o=t._progressHandlerAt(r),s=t._promiseAt(r),a=t._receiverAt(r);s instanceof e&&s._setIsMigrated(),this._addCallbacks(n,i,o,s,a)},e.prototype._addCallbacks=function(t,e,r,n,i){var o=this._length();if(o>=131066&&(o=0,this._setLength(0)),0===o)this._promise0=n,void 0!==i&&(this._receiver0=i),"function"!=typeof t||this._isCarryingStackTrace()||(this._fulfillmentHandler0=t),"function"==typeof e&&(this._rejectionHandler0=e),"function"==typeof r&&(this._progressHandler0=r);else{var s=5*o-5;this[s+3]=n,this[s+4]=i,"function"==typeof t&&(this[s+0]=t),"function"==typeof e&&(this[s+1]=e),"function"==typeof r&&(this[s+2]=r)}return this._setLength(o+1),o},e.prototype._setProxyHandlers=function(t,e){var r=this._length();if(r>=131066&&(r=0,this._setLength(0)),0===r)this._promise0=e,this._receiver0=t;else{var n=5*r-5;this[n+3]=e,this[n+4]=t}this._setLength(r+1)},e.prototype._proxyPromiseArray=function(t,e){this._setProxyHandlers(t,e)},e.prototype._resolveCallback=function(t,r){if(!this._isFollowingOrFulfilledOrRejected()){if(t===this)return this._rejectCallback(n(),!1,!0);var i=f(t,this);if(!(i instanceof e))return this._fulfill(t);var o=1|(r?4:0);this._propagateFrom(i,o);var s=i._target();if(s._isPending()){for(var a=this._length(),u=0;a>u;++u)s._migrateCallbacks(this,u);this._setFollowing(),this._setLength(0),this._setFollowee(s)}else s._isFulfilled()?this._fulfillUnchecked(s._value()):this._rejectUnchecked(s._reason(),s._getCarriedStackTrace())}},e.prototype._rejectCallback=function(t,e,r){r||s.markAsOriginatingFromRejection(t);var n=s.ensureErrorObject(t),i=n===t;this._attachExtraTrace(n,e?i:!1),this._reject(t,i?void 0:n)},e.prototype._resolveFromResolver=function(t){var e=this;this._captureStackTrace(),this._pushContext();var r=!0,n=w(t)(function(t){null!==e&&(e._resolveCallback(t),e=null)},function(t){null!==e&&(e._rejectCallback(t,r),e=null)});r=!1,this._popContext(),void 0!==n&&n===b&&null!==e&&(e._rejectCallback(n.e,!0,!0),e=null)},e.prototype._settlePromiseFromHandler=function(t,e,r,i){if(!i._isRejected()){i._pushContext();var o;if(o=e!==h||this._isRejected()?w(t).call(e,r):w(t).apply(this._boundTo,r),i._popContext(),o===b||o===i||o===p){var s=o===i?n():o.e;i._rejectCallback(s,!1,!0)}else i._resolveCallback(o)}},e.prototype._target=function(){for(var t=this;t._isFollowing();)t=t._followee();return t},e.prototype._followee=function(){return this._rejectionHandler0},e.prototype._setFollowee=function(t){this._rejectionHandler0=t},e.prototype._cleanValues=function(){this._cancellable()&&(this._cancellationParent=void 0)},e.prototype._propagateFrom=function(t,e){(1&e)>0&&t._cancellable()&&(this._setCancellable(),this._cancellationParent=t),(4&e)>0&&t._isBound()&&this._setBoundTo(t._boundTo)},e.prototype._fulfill=function(t){this._isFollowingOrFulfilledOrRejected()||this._fulfillUnchecked(t)},e.prototype._reject=function(t,e){this._isFollowingOrFulfilledOrRejected()||this._rejectUnchecked(t,e)},e.prototype._settlePromiseAt=function(t){var r=this._promiseAt(t),n=r instanceof e;if(n&&r._isMigrated())return r._unsetIsMigrated(),a.invoke(this._settlePromiseAt,this,t);var i=this._isFulfilled()?this._fulfillmentHandlerAt(t):this._rejectionHandlerAt(t),o=this._isCarryingStackTrace()?this._getCarriedStackTrace():void 0,s=this._settledValue,u=this._receiverAt(t);this._clearCallbackDataAtIndex(t),"function"==typeof i?n?this._settlePromiseFromHandler(i,u,s,r):i.call(u,s,r):u instanceof _?u._isResolved()||(this._isFulfilled()?u._promiseFulfilled(s,r):u._promiseRejected(s,r)):n&&(this._isFulfilled()?r._fulfill(s):r._reject(s,o)),t>=4&&4===(31&t)&&a.invokeLater(this._setLength,this,0)},e.prototype._clearCallbackDataAtIndex=function(t){if(0===t)this._isCarryingStackTrace()||(this._fulfillmentHandler0=void 0),this._rejectionHandler0=this._progressHandler0=this._receiver0=this._promise0=void 0;else{var e=5*t-5;this[e+3]=this[e+4]=this[e+0]=this[e+1]=this[e+2]=void 0}},e.prototype._isSettlePromisesQueued=function(){return-1073741824===(-1073741824&this._bitField)},e.prototype._setSettlePromisesQueued=function(){this._bitField=-1073741824|this._bitField},e.prototype._unsetSettlePromisesQueued=function(){this._bitField=1073741823&this._bitField},e.prototype._queueSettlePromises=function(){a.settlePromises(this),this._setSettlePromisesQueued()},e.prototype._fulfillUnchecked=function(t){if(t===this){var e=n();return this._attachExtraTrace(e),this._rejectUnchecked(e,void 0)}this._setFulfilled(),this._settledValue=t,this._cleanValues(),this._length()>0&&this._queueSettlePromises()},e.prototype._rejectUncheckedCheckError=function(t){var e=s.ensureErrorObject(t);this._rejectUnchecked(t,e===t?void 0:e)},e.prototype._rejectUnchecked=function(t,e){if(t===this){var r=n();return this._attachExtraTrace(r),this._rejectUnchecked(r)}return this._setRejected(),this._settledValue=t,this._cleanValues(),this._isFinal()?void a.throwLater(function(t){throw"stack"in t&&a.invokeFirst(d.unhandledRejection,void 0,t),t},void 0===e?t:e):(void 0!==e&&e!==t&&this._setCarriedStackTrace(e),void(this._length()>0?this._queueSettlePromises():this._ensurePossibleRejectionHandled()))},e.prototype._settlePromises=function(){this._unsetSettlePromisesQueued();for(var t=this._length(),e=0;t>e;e++)this._settlePromiseAt(e)},e._makeSelfResolutionError=n,t("./progress.js")(e,_),t("./method.js")(e,l,f,o),t("./bind.js")(e,l,f),t("./finally.js")(e,p,f),t("./direct_resolve.js")(e),t("./synchronous_inspection.js")(e),t("./join.js")(e,_,f,l),e.Promise=e,t("./map.js")(e,_,o,f,l),t("./cancel.js")(e),t("./using.js")(e,o,f,y),t("./generators.js")(e,o,l,f),t("./nodeify.js")(e),t("./call_get.js")(e),t("./props.js")(e,_,f,o),t("./race.js")(e,l,f,o),t("./reduce.js")(e,_,o,f,l),t("./settle.js")(e,_),t("./some.js")(e,_,o),t("./promisify.js")(e,l),t("./any.js")(e),t("./each.js")(e,l),t("./timers.js")(e,l),t("./filter.js")(e,l),s.toFastProperties(e),s.toFastProperties(e.prototype),r({a:1}),r({b:2}),r({c:3}),r(1),r(function(){}),r(void 0),r(!1),r(new e(l)),d.setBounds(a.firstLineError,s.lastLineError),e}},{"./any.js":1,"./async.js":2,"./bind.js":3,"./call_get.js":5,"./cancel.js":6,"./captured_trace.js":7,"./catch_filter.js":8,"./context.js":9,"./debuggability.js":10,"./direct_resolve.js":11,"./each.js":12,"./errors.js":13,"./filter.js":15,"./finally.js":16,"./generators.js":17,"./join.js":18,"./map.js":19,"./method.js":20,"./nodeify.js":21,"./progress.js":22,"./promise_array.js":24,"./promise_resolver.js":25,"./promisify.js":26,"./props.js":27,"./race.js":29,"./reduce.js":30,"./settle.js":32,"./some.js":33,"./synchronous_inspection.js":34,"./thenables.js":35,"./timers.js":36,"./using.js":37,"./util.js":38}],24:[function(t,e){"use strict";e.exports=function(e,r,n,i){function o(t){switch(t){case-2:return[];case-3:return{}}}function s(t){var n,i=this._promise=new e(r);t instanceof e&&(n=t,i._propagateFrom(n,5)),this._values=t,this._length=0,this._totalResolved=0,this._init(void 0,-2)}var a=t("./util.js"),u=a.isArray;return s.prototype.length=function(){return this._length},s.prototype.promise=function(){return this._promise},s.prototype._init=function c(t,r){var s=n(this._values,this._promise);if(s instanceof e){if(s=s._target(),this._values=s,!s._isFulfilled())return s._isPending()?void s._then(c,this._reject,void 0,this,r):void this._reject(s._reason());if(s=s._value(),!u(s)){var a=new e.TypeError("expecting an array, a promise or a thenable\n\n See http://goo.gl/s8MMhc\n");return void this.__hardReject__(a)}}else if(!u(s))return void this._promise._reject(i("expecting an array, a promise or a thenable\n\n See http://goo.gl/s8MMhc\n")._reason());if(0===s.length)return void(-5===r?this._resolveEmptyArray():this._resolve(o(r)));var l=this.getActualLength(s.length);this._length=l,this._values=this.shouldCopyValues()?new Array(l):this._values;for(var h=this._promise,p=0;l>p;++p){var f=this._isResolved(),_=n(s[p],h);_ instanceof e?(_=_._target(),f?_._unsetRejectionIsUnhandled():_._isPending()?_._proxyPromiseArray(this,p):_._isFulfilled()?this._promiseFulfilled(_._value(),p):this._promiseRejected(_._reason(),p)):f||this._promiseFulfilled(_,p)}},s.prototype._isResolved=function(){return null===this._values},s.prototype._resolve=function(t){this._values=null,this._promise._fulfill(t)},s.prototype.__hardReject__=s.prototype._reject=function(t){this._values=null,this._promise._rejectCallback(t,!1,!0)},s.prototype._promiseProgressed=function(t,e){this._promise._progress({index:e,value:t})},s.prototype._promiseFulfilled=function(t,e){this._values[e]=t;var r=++this._totalResolved;r>=this._length&&this._resolve(this._values)},s.prototype._promiseRejected=function(t){this._totalResolved++,this._reject(t)},s.prototype.shouldCopyValues=function(){return!0},s.prototype.getActualLength=function(t){return t},s}},{"./util.js":38}],25:[function(t,e){"use strict";function r(t){return t instanceof Error&&p.getPrototypeOf(t)===Error.prototype}function n(t){var e;if(r(t)){e=new l(t),e.name=t.name,e.message=t.message,e.stack=t.stack;for(var n=p.keys(t),i=0;i<n.length;++i){var o=n[i];f.test(o)||(e[o]=t[o])}return e}return s.markAsOriginatingFromRejection(t),t}function i(t){return function(e,r){if(null!==t){if(e){var i=n(a(e));t._attachExtraTrace(i),t._reject(i)}else if(arguments.length>2){for(var o=arguments.length,s=new Array(o-1),u=1;o>u;++u)s[u-1]=arguments[u];t._fulfill(s)}else t._fulfill(r);t=null}}}var o,s=t("./util.js"),a=s.maybeWrapAsError,u=t("./errors.js"),c=u.TimeoutError,l=u.OperationalError,h=s.haveGetters,p=t("./es5.js"),f=/^(?:name|message|stack|cause)$/;if(o=h?function(t){this.promise=t}:function(t){this.promise=t,this.asCallback=i(t),this.callback=this.asCallback},h){var _={get:function(){return i(this.promise)}};p.defineProperty(o.prototype,"asCallback",_),p.defineProperty(o.prototype,"callback",_)}o._nodebackForPromise=i,o.prototype.toString=function(){return"[object PromiseResolver]"},o.prototype.resolve=o.prototype.fulfill=function(t){if(!(this instanceof o))throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\n\n See http://goo.gl/sdkXL9\n");this.promise._resolveCallback(t)},o.prototype.reject=function(t){if(!(this instanceof o))throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\n\n See http://goo.gl/sdkXL9\n");this.promise._rejectCallback(t)},o.prototype.progress=function(t){if(!(this instanceof o))throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\n\n See http://goo.gl/sdkXL9\n");this.promise._progress(t)},o.prototype.cancel=function(t){this.promise.cancel(t)},o.prototype.timeout=function(){this.reject(new c("timeout"))},o.prototype.isResolved=function(){return this.promise.isResolved()},o.prototype.toJSON=function(){return this.promise.toJSON()},e.exports=o},{"./errors.js":13,"./es5.js":14,"./util.js":38}],26:[function(t,e){"use strict";e.exports=function(e,r){function n(t){return!b.test(t)}function i(t){try{return t.__isPromisified__===!0}catch(e){return!1}}function o(t,e,r){var n=f.getDataPropertyOrDefault(t,e+r,j);return n?i(n):!1}function s(t,e,r){for(var n=0;n<t.length;n+=2){var i=t[n];if(r.test(i))for(var o=i.replace(r,""),s=0;s<t.length;s+=2)if(t[s]===o)throw new g("Cannot promisify an API that has normal methods with '%s'-suffix\n\n See http://goo.gl/iWrZbw\n".replace("%s",e))}}function a(t,e,r,n){for(var a=f.inheritedDataKeys(t),u=[],c=0;c<a.length;++c){var l=a[c],h=t[l],p=n===w?!0:w(l,h,t);"function"!=typeof h||i(h)||o(t,l,e)||!n(l,h,t,p)||u.push(l,h)}return s(u,e,r),u}function u(t,n,i,o){function s(){var i=n;n===p&&(i=this);var o=new e(r);o._captureStackTrace();var s="string"==typeof u&&this!==a?this[u]:t,c=_(o);try{s.apply(i,d(arguments,c))}catch(l){o._rejectCallback(v(l),!0,!0)}return o}var a=function(){return this}(),u=t;return"string"==typeof u&&(t=o),s.__isPromisified__=!0,s}function c(t,e,r,n){for(var i=new RegExp(k(e)+"$"),o=a(t,e,i,r),s=0,u=o.length;u>s;s+=2){var c=o[s],l=o[s+1],h=c+e;t[h]=n===E?E(c,p,c,l,e):n(l,function(){return E(c,p,c,l,e)})}return f.toFastProperties(t),t}function l(t,e){return E(t,e,void 0,t)}var h,p={},f=t("./util.js"),_=t("./promise_resolver.js")._nodebackForPromise,d=f.withAppended,v=f.maybeWrapAsError,y=f.canEvaluate,g=t("./errors").TypeError,m="Async",j={__isPromisified__:!0},b=/^(?:length|name|arguments|caller|callee|prototype|__isPromisified__)$/,w=function(t,e){return f.isIdentifier(t)&&"_"!==t.charAt(0)&&!f.isClass(e)},k=function(t){return t.replace(/([$])/,"\\$")},E=y?h:u;e.promisify=function(t,e){if("function"!=typeof t)throw new g("fn must be a function\n\n See http://goo.gl/916lJJ\n");if(i(t))return t;var r=l(t,arguments.length<2?p:e);return f.copyDescriptors(t,r,n),r},e.promisifyAll=function(t,e){if("function"!=typeof t&&"object"!=typeof t)throw new g("the target of promisifyAll must be an object or a function\n\n See http://goo.gl/9ITlV0\n");e=Object(e);var r=e.suffix;"string"!=typeof r&&(r=m);var n=e.filter;"function"!=typeof n&&(n=w);var i=e.promisifier;if("function"!=typeof i&&(i=E),!f.isIdentifier(r))throw new RangeError("suffix must be a valid identifier\n\n See http://goo.gl/8FZo5V\n");for(var o=f.inheritedDataKeys(t),s=0;s<o.length;++s){var a=t[o[s]];"constructor"!==o[s]&&f.isClass(a)&&(c(a.prototype,r,n,i),c(a,r,n,i))}return c(t,r,n,i)}}},{"./errors":13,"./promise_resolver.js":25,"./util.js":38}],27:[function(t,e){"use strict";e.exports=function(e,r,n,i){function o(t){for(var e=c.keys(t),r=e.length,n=new Array(2*r),i=0;r>i;++i){var o=e[i];n[i]=t[o],n[i+r]=o}this.constructor$(n)}function s(t){var r,s=n(t);return u(s)?(r=s instanceof e?s._then(e.props,void 0,void 0,void 0,void 0):new o(s).promise(),s instanceof e&&r._propagateFrom(s,4),r):i("cannot await properties of a non-object\n\n See http://goo.gl/OsFKC8\n")}var a=t("./util.js"),u=a.isObject,c=t("./es5.js");a.inherits(o,r),o.prototype._init=function(){this._init$(void 0,-3)},o.prototype._promiseFulfilled=function(t,e){this._values[e]=t;var r=++this._totalResolved;if(r>=this._length){for(var n={},i=this.length(),o=0,s=this.length();s>o;++o)n[this._values[o+i]]=this._values[o];this._resolve(n)}},o.prototype._promiseProgressed=function(t,e){this._promise._progress({key:this._values[e+this.length()],value:t})},o.prototype.shouldCopyValues=function(){return!1},o.prototype.getActualLength=function(t){return t>>1},e.prototype.props=function(){return s(this)},e.props=function(t){return s(t)}}},{"./es5.js":14,"./util.js":38}],28:[function(t,e){"use strict";function r(t,e,r,n,i){for(var o=0;i>o;++o)r[o+n]=t[o+e],t[o+e]=void 0}function n(t){this._capacity=t,this._length=0,this._front=0}n.prototype._willBeOverCapacity=function(t){return this._capacity<t},n.prototype._pushOne=function(t){var e=this.length();this._checkCapacity(e+1);var r=this._front+e&this._capacity-1;this[r]=t,this._length=e+1},n.prototype._unshiftOne=function(t){var e=this._capacity;this._checkCapacity(this.length()+1);var r=this._front,n=(r-1&e-1^e)-e;this[n]=t,this._front=n,this._length=this.length()+1},n.prototype.unshift=function(t,e,r){this._unshiftOne(r),this._unshiftOne(e),this._unshiftOne(t)},n.prototype.push=function(t,e,r){var n=this.length()+3;if(this._willBeOverCapacity(n))return this._pushOne(t),this._pushOne(e),void this._pushOne(r);var i=this._front+n-3;this._checkCapacity(n);var o=this._capacity-1;this[i+0&o]=t,this[i+1&o]=e,this[i+2&o]=r,this._length=n},n.prototype.shift=function(){var t=this._front,e=this[t];return this[t]=void 0,this._front=t+1&this._capacity-1,this._length--,e},n.prototype.length=function(){return this._length},n.prototype._checkCapacity=function(t){this._capacity<t&&this._resizeTo(this._capacity<<1)},n.prototype._resizeTo=function(t){var e=this._capacity;this._capacity=t;var n=this._front,i=this._length,o=n+i&e-1;r(this,0,this,e,o)},e.exports=n},{}],29:[function(t,e){"use strict";e.exports=function(e,r,n,i){function o(t,o){var u=n(t);if(u instanceof e)return a(u);if(!s(t))return i("expecting an array, a promise or a thenable\n\n See http://goo.gl/s8MMhc\n");var c=new e(r);void 0!==o&&c._propagateFrom(o,5);for(var l=c._fulfill,h=c._reject,p=0,f=t.length;f>p;++p){var _=t[p];(void 0!==_||p in t)&&e.cast(_)._then(l,h,void 0,c,null)}return c}var s=t("./util.js").isArray,a=function(t){return t.then(function(e){return o(e,t)})};e.race=function(t){return o(t,void 0)},e.prototype.race=function(){return o(this,void 0)}}},{"./util.js":38}],30:[function(t,e){"use strict";e.exports=function(e,r,n,i,o){function s(t,r,n,s){this.constructor$(t),this._promise._captureStackTrace(),this._preservedValues=s===o?[]:null,this._zerothIsAccum=void 0===n,this._gotAccum=!1,this._reducingIndex=this._zerothIsAccum?1:0,this._valuesPhase=void 0;var u=i(n,this._promise),l=!1,h=u instanceof e;h&&(u=u._target(),u._isPending()?u._proxyPromiseArray(this,-1):u._isFulfilled()?(n=u._value(),this._gotAccum=!0):(this._reject(u._reason()),l=!0)),h||this._zerothIsAccum||(this._gotAccum=!0),this._callback=r,this._accum=n,l||c.invoke(a,this,void 0)}function a(){this._init$(void 0,-5)}function u(t,e,r,i){if("function"!=typeof e)return n("fn must be a function\n\n See http://goo.gl/916lJJ\n");var o=new s(t,e,r,i);return o.promise()}var c=t("./async.js"),l=t("./util.js"),h=l.tryCatch,p=l.errorObj;l.inherits(s,r),s.prototype._init=function(){},s.prototype._resolveEmptyArray=function(){(this._gotAccum||this._zerothIsAccum)&&this._resolve(null!==this._preservedValues?[]:this._accum)},s.prototype._promiseFulfilled=function(t,r){var n=this._values;n[r]=t;var o,s=this.length(),a=this._preservedValues,u=null!==a,c=this._gotAccum,l=this._valuesPhase;if(!l)for(l=this._valuesPhase=new Array(s),o=0;s>o;++o)l[o]=0;if(o=l[r],0===r&&this._zerothIsAccum?(this._accum=t,this._gotAccum=c=!0,l[r]=0===o?1:2):-1===r?(this._accum=t,this._gotAccum=c=!0):0===o?l[r]=1:(l[r]=2,this._accum=t),c){for(var f,_=this._callback,d=this._promise._boundTo,v=this._reducingIndex;s>v;++v)if(o=l[v],2!==o){if(1!==o)return;if(t=n[v],this._promise._pushContext(),u?(a.push(t),f=h(_).call(d,t,v,s)):f=h(_).call(d,this._accum,t,v,s),this._promise._popContext(),f===p)return this._reject(f.e);var y=i(f,this._promise);if(y instanceof e){if(y=y._target(),y._isPending())return l[v]=4,y._proxyPromiseArray(this,v);if(!y._isFulfilled())return this._reject(y._reason());f=y._value()}this._reducingIndex=v+1,this._accum=f}else this._reducingIndex=v+1;this._resolve(u?a:this._accum)}},e.prototype.reduce=function(t,e){return u(this,t,e,null)},e.reduce=function(t,e,r,n){return u(t,e,r,n)}}},{"./async.js":2,"./util.js":38}],31:[function(t,e){"use strict";var r,n=function(){throw new Error("No async scheduler available\n\n See http://goo.gl/m3OTXk\n")};if(t("./util.js").isNode){var i=process.versions.node.split(".").map(Number);r=0===i[0]&&i[1]>10||i[0]>0?global.setImmediate:process.nextTick,r||(r="undefined"!=typeof setImmediate?setImmediate:"undefined"!=typeof setTimeout?setTimeout:n)}else"undefined"!=typeof MutationObserver?(r=function(t){var e=document.createElement("div"),r=new MutationObserver(t);return r.observe(e,{attributes:!0}),function(){e.classList.toggle("foo")}},r.isStatic=!0):r="undefined"!=typeof setImmediate?function(t){setImmediate(t)}:"undefined"!=typeof setTimeout?function(t){setTimeout(t,0)}:n;e.exports=r},{"./util.js":38}],32:[function(t,e){"use strict";e.exports=function(e,r){function n(t){this.constructor$(t)}var i=e.PromiseInspection,o=t("./util.js");o.inherits(n,r),n.prototype._promiseResolved=function(t,e){this._values[t]=e;var r=++this._totalResolved;r>=this._length&&this._resolve(this._values)},n.prototype._promiseFulfilled=function(t,e){var r=new i;r._bitField=268435456,r._settledValue=t,this._promiseResolved(e,r)},n.prototype._promiseRejected=function(t,e){var r=new i;r._bitField=134217728,r._settledValue=t,this._promiseResolved(e,r)},e.settle=function(t){return new n(t).promise()},e.prototype.settle=function(){return new n(this).promise()}}},{"./util.js":38}],33:[function(t,e){"use strict";e.exports=function(e,r,n){function i(t){this.constructor$(t),this._howMany=0,this._unwrap=!1,this._initialized=!1}function o(t,e){if((0|e)!==e||0>e)return n("expecting a positive integer\n\n See http://goo.gl/1wAmHx\n");var r=new i(t),o=r.promise();return r.setHowMany(e),r.init(),o}var s=t("./util.js"),a=t("./errors.js").RangeError,u=t("./errors.js").AggregateError,c=s.isArray;s.inherits(i,r),i.prototype._init=function(){if(this._initialized){if(0===this._howMany)return void this._resolve([]);this._init$(void 0,-5);var t=c(this._values);!this._isResolved()&&t&&this._howMany>this._canPossiblyFulfill()&&this._reject(this._getRangeError(this.length()))}},i.prototype.init=function(){this._initialized=!0,this._init()},i.prototype.setUnwrap=function(){this._unwrap=!0},i.prototype.howMany=function(){return this._howMany},i.prototype.setHowMany=function(t){this._howMany=t},i.prototype._promiseFulfilled=function(t){this._addFulfilled(t),this._fulfilled()===this.howMany()&&(this._values.length=this.howMany(),this._resolve(1===this.howMany()&&this._unwrap?this._values[0]:this._values))},i.prototype._promiseRejected=function(t){if(this._addRejected(t),this.howMany()>this._canPossiblyFulfill()){for(var e=new u,r=this.length();r<this._values.length;++r)e.push(this._values[r]);this._reject(e)}},i.prototype._fulfilled=function(){return this._totalResolved},i.prototype._rejected=function(){return this._values.length-this.length()},i.prototype._addRejected=function(t){this._values.push(t)},i.prototype._addFulfilled=function(t){this._values[this._totalResolved++]=t},i.prototype._canPossiblyFulfill=function(){return this.length()-this._rejected()},i.prototype._getRangeError=function(t){var e="Input array must contain at least "+this._howMany+" items but contains only "+t+" items";return new a(e)},i.prototype._resolveEmptyArray=function(){this._reject(this._getRangeError(0))},e.some=function(t,e){return o(t,e)},e.prototype.some=function(t){return o(this,t)},e._SomePromiseArray=i}},{"./errors.js":13,"./util.js":38}],34:[function(t,e){"use strict";e.exports=function(t){function e(t){void 0!==t?(t=t._target(),this._bitField=t._bitField,this._settledValue=t._settledValue):(this._bitField=0,this._settledValue=void 0)}e.prototype.value=function(){if(!this.isFulfilled())throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\n\n See http://goo.gl/hc1DLj\n");return this._settledValue},e.prototype.error=e.prototype.reason=function(){if(!this.isRejected())throw new TypeError("cannot get rejection reason of a non-rejected promise\n\n See http://goo.gl/hPuiwB\n");return this._settledValue},e.prototype.isFulfilled=t.prototype._isFulfilled=function(){return(268435456&this._bitField)>0},e.prototype.isRejected=t.prototype._isRejected=function(){return(134217728&this._bitField)>0},e.prototype.isPending=t.prototype._isPending=function(){return 0===(402653184&this._bitField)},e.prototype.isResolved=t.prototype._isResolved=function(){return(402653184&this._bitField)>0},t.prototype.isPending=function(){return this._target()._isPending()},t.prototype.isRejected=function(){return this._target()._isRejected()},t.prototype.isFulfilled=function(){return this._target()._isFulfilled()},t.prototype.isResolved=function(){return this._target()._isResolved()},t.prototype._value=function(){return this._settledValue},t.prototype._reason=function(){return this._unsetRejectionIsUnhandled(),this._settledValue},t.prototype.value=function(){var t=this._target();if(!t.isFulfilled())throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\n\n See http://goo.gl/hc1DLj\n");return t._settledValue},t.prototype.reason=function(){var t=this._target();if(!t.isRejected())throw new TypeError("cannot get rejection reason of a non-rejected promise\n\n See http://goo.gl/hPuiwB\n");return t._unsetRejectionIsUnhandled(),t._settledValue},t.PromiseInspection=e}},{}],35:[function(t,e){"use strict";e.exports=function(e,r){function n(t,n){if(c(t)){if(t instanceof e)return t;if(o(t)){var l=new e(r);return t._then(l._fulfillUnchecked,l._rejectUncheckedCheckError,l._progressUnchecked,l,null),l}var h=a.tryCatch(i)(t);if(h===u){n&&n._pushContext();var l=e.reject(h.e);return n&&n._popContext(),l}if("function"==typeof h)return s(t,h,n)}return t}function i(t){return t.then}function o(t){return l.call(t,"_promise0")}function s(t,n,i){function o(r){l&&(t===r?l._rejectCallback(e._makeSelfResolutionError(),!1,!0):l._resolveCallback(r),l=null)}function s(t){l&&(l._rejectCallback(t,p,!0),l=null)}function c(t){l&&"function"==typeof l._progress&&l._progress(t)}var l=new e(r),h=l;i&&i._pushContext(),l._captureStackTrace(),i&&i._popContext();var p=!0,f=a.tryCatch(n).call(t,o,s,c);return p=!1,l&&f===u&&(l._rejectCallback(f.e,!0,!0),l=null),h}var a=t("./util.js"),u=a.errorObj,c=a.isObject,l={}.hasOwnProperty;return n}},{"./util.js":38}],36:[function(t,e){"use strict";e.exports=function(e,r){function n(t){var e=this;return e instanceof Number&&(e=+e),clearTimeout(e),t}function i(t){var e=this;throw e instanceof Number&&(e=+e),clearTimeout(e),t}var o=t("./util.js"),s=e.TimeoutError,a=function(t,e){if(t.isPending()){"string"!=typeof e&&(e="operation timed out");var r=new s(e);o.markAsOriginatingFromRejection(r),t._attachExtraTrace(r),t._cancel(r)}},u=function(t){return c(+this).thenReturn(t)},c=e.delay=function(t,n){if(void 0===n){n=t,t=void 0;var i=new e(r);return setTimeout(function(){i._fulfill()},n),i}return n=+n,e.resolve(t)._then(u,null,null,n,void 0)};e.prototype.delay=function(t){return c(this,t)},e.prototype.timeout=function(t,e){t=+t;var r=this.then().cancellable();r._cancellationParent=this;var o=setTimeout(function(){a(r,e)},t);return r._then(n,i,void 0,o,void 0)}}},{"./util.js":38}],37:[function(t,e){"use strict";e.exports=function(e,r,n,i){function o(t){for(var r=t.length,n=0;r>n;++n){var i=t[n];if(i.isRejected())return e.reject(i.error());t[n]=i._settledValue}return t}function s(t){setTimeout(function(){throw t},0)}function a(t){var e=n(t);return e!==t&&"function"==typeof t._isDisposable&&"function"==typeof t._getDisposer&&t._isDisposable()&&e._setDisposable(t._getDisposer()),e}function u(t,r){function i(){if(o>=u)return c.resolve();var l=a(t[o++]);if(l instanceof e&&l._isDisposable()){try{l=n(l._getDisposer().tryDispose(r),t.promise)}catch(h){return s(h)}if(l instanceof e)return l._then(i,s,null,null,null)}i()}var o=0,u=t.length,c=e.defer();return i(),c.promise}function c(t){var e=new v;return e._settledValue=t,e._bitField=268435456,u(this,e).thenReturn(t)}function l(t){var e=new v;return e._settledValue=t,e._bitField=134217728,u(this,e).thenThrow(t)}function h(t,e,r){this._data=t,this._promise=e,this._context=r}function p(t,e,r){this.constructor$(t,e,r)}function f(t){return h.isDisposer(t)?(this.resources[this.index]._setDisposable(t),t.promise()):t}var _=t("./errors.js").TypeError,d=t("./util.js").inherits,v=e.PromiseInspection;h.prototype.data=function(){return this._data},h.prototype.promise=function(){return this._promise},h.prototype.resource=function(){return this.promise().isFulfilled()?this.promise().value():null},h.prototype.tryDispose=function(t){var e=this.resource(),r=this._context;void 0!==r&&r._pushContext();var n=null!==e?this.doDispose(e,t):null;return void 0!==r&&r._popContext(),this._promise._unsetDisposable(),this._data=null,n},h.isDisposer=function(t){return null!=t&&"function"==typeof t.resource&&"function"==typeof t.tryDispose},d(p,h),p.prototype.doDispose=function(t,e){var r=this.data();return r.call(t,t,e)},e.using=function(){var t=arguments.length;if(2>t)return r("you must pass at least 2 arguments to Promise.using");var i=arguments[t-1];if("function"!=typeof i)return r("fn must be a function\n\n See http://goo.gl/916lJJ\n");t--;for(var s=new Array(t),a=0;t>a;++a){var u=arguments[a];if(h.isDisposer(u)){var p=u;u=u.promise(),u._setDisposable(p)}else{var _=n(u);_ instanceof e&&(u=_._then(f,null,null,{resources:s,index:a},void 0))}s[a]=u}var d=e.settle(s).then(o).then(function(t){d._pushContext();var e;try{e=i.apply(void 0,t)}finally{d._popContext()}return e})._then(c,l,void 0,s,void 0);return s.promise=d,d},e.prototype._setDisposable=function(t){this._bitField=262144|this._bitField,this._disposer=t},e.prototype._isDisposable=function(){return(262144&this._bitField)>0},e.prototype._getDisposer=function(){return this._disposer},e.prototype._unsetDisposable=function(){this._bitField=-262145&this._bitField,this._disposer=void 0},e.prototype.disposer=function(t){if("function"==typeof t)return new p(t,this,i());throw new _}}},{"./errors.js":13,"./util.js":38}],38:[function(t,e,r){"use strict";function n(){try{return T.apply(this,arguments)}catch(t){return F.e=t,F}}function i(t){return T=t,n}function o(t){return null==t||t===!0||t===!1||"string"==typeof t||"number"==typeof t
+}function s(t){return!o(t)}function a(t){return o(t)?new Error(v(t)):t}function u(t,e){var r,n=t.length,i=new Array(n+1);for(r=0;n>r;++r)i[r]=t[r];return i[r]=e,i}function c(t,e,r){if(!w.isES5)return{}.hasOwnProperty.call(t,e)?t[e]:void 0;var n=Object.getOwnPropertyDescriptor(t,e);return null!=n?null==n.get&&null==n.set?n.value:r:void 0}function l(t,e,r){if(o(t))return t;var n={value:r,configurable:!0,enumerable:!1,writable:!0};return w.defineProperty(t,e,n),t}function h(t){throw t}function p(t){try{if("function"==typeof t){var e=w.names(t.prototype);return w.isES5?e.length>1:e.length>0&&!(1===e.length&&"constructor"===e[0])}return!1}catch(r){return!1}}function f(t){function e(){}e.prototype=t;for(var r=8;r--;)new e;return t}function _(t){return R.test(t)}function d(t,e,r){for(var n=new Array(t),i=0;t>i;++i)n[i]=e+i+r;return n}function v(t){try{return t+""}catch(e){return"[no string representation]"}}function y(t){try{l(t,"isOperational",!0)}catch(e){}}function g(t){return null==t?!1:t instanceof Error.__BluebirdErrorTypes__.OperationalError||t.isOperational===!0}function m(t){return t instanceof Error&&w.propertyIsWritable(t,"stack")}function j(t){return{}.toString.call(t)}function b(t,e,r){for(var n=w.names(t),i=0;i<n.length;++i){var o=n[i];r(o)&&w.defineProperty(e,o,w.getDescriptor(t,o))}}var w=t("./es5.js"),k="undefined"==typeof navigator,E=function(){try{var t={};return w.defineProperty(t,"f",{get:function(){return 3}}),3===t.f}catch(e){return!1}}(),F={e:{}},T,C=function(t,e){function r(){this.constructor=t,this.constructor$=e;for(var r in e.prototype)n.call(e.prototype,r)&&"$"!==r.charAt(r.length-1)&&(this[r+"$"]=e.prototype[r])}var n={}.hasOwnProperty;return r.prototype=e.prototype,t.prototype=new r,t.prototype},x=function(){return"string"!==this}.call("string"),P=function(){if(w.isES5){var t=Object.prototype,e=Object.getOwnPropertyNames;return function(r){for(var n=[],i=Object.create(null);null!=r&&r!==t;){var o;try{o=e(r)}catch(s){return n}for(var a=0;a<o.length;++a){var u=o[a];if(!i[u]){i[u]=!0;var c=Object.getOwnPropertyDescriptor(r,u);null!=c&&null==c.get&&null==c.set&&n.push(u)}}r=w.getPrototypeOf(r)}return n}}return function(t){var e=[];for(var r in t)e.push(r);return e}}(),R=/^[a-z$_][a-z$_0-9]*$/i,A=function(){return"stack"in new Error?function(t){return m(t)?t:new Error(v(t))}:function(t){if(m(t))return t;try{throw new Error(v(t))}catch(e){return e}}}(),S={isClass:p,isIdentifier:_,inheritedDataKeys:P,getDataPropertyOrDefault:c,thrower:h,isArray:w.isArray,haveGetters:E,notEnumerableProp:l,isPrimitive:o,isObject:s,canEvaluate:k,errorObj:F,tryCatch:i,inherits:C,withAppended:u,maybeWrapAsError:a,wrapsPrimitiveReceiver:x,toFastProperties:f,filledRange:d,toString:v,canAttachTrace:m,ensureErrorObject:A,originatesFromRejection:g,markAsOriginatingFromRejection:y,classString:j,copyDescriptors:b,hasDevTools:"undefined"!=typeof chrome&&chrome&&"function"==typeof chrome.loadTimes,isNode:"undefined"!=typeof process&&"[object process]"===j(process).toLowerCase()};try{throw new Error}catch(O){S.lastLineError=O}e.exports=S},{"./es5.js":14}],39:[function(t,e){function r(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function n(t){return"function"==typeof t}function i(t){return"number"==typeof t}function o(t){return"object"==typeof t&&null!==t}function s(t){return void 0===t}e.exports=r,r.EventEmitter=r,r.prototype._events=void 0,r.prototype._maxListeners=void 0,r.defaultMaxListeners=10,r.prototype.setMaxListeners=function(t){if(!i(t)||0>t||isNaN(t))throw TypeError("n must be a positive number");return this._maxListeners=t,this},r.prototype.emit=function(t){var e,r,i,a,u,c;if(this._events||(this._events={}),"error"===t&&(!this._events.error||o(this._events.error)&&!this._events.error.length)){if(e=arguments[1],e instanceof Error)throw e;throw TypeError('Uncaught, unspecified "error" event.')}if(r=this._events[t],s(r))return!1;if(n(r))switch(arguments.length){case 1:r.call(this);break;case 2:r.call(this,arguments[1]);break;case 3:r.call(this,arguments[1],arguments[2]);break;default:for(i=arguments.length,a=new Array(i-1),u=1;i>u;u++)a[u-1]=arguments[u];r.apply(this,a)}else if(o(r)){for(i=arguments.length,a=new Array(i-1),u=1;i>u;u++)a[u-1]=arguments[u];for(c=r.slice(),i=c.length,u=0;i>u;u++)c[u].apply(this,a)}return!0},r.prototype.addListener=function(t,e){var i;if(!n(e))throw TypeError("listener must be a function");if(this._events||(this._events={}),this._events.newListener&&this.emit("newListener",t,n(e.listener)?e.listener:e),this._events[t]?o(this._events[t])?this._events[t].push(e):this._events[t]=[this._events[t],e]:this._events[t]=e,o(this._events[t])&&!this._events[t].warned){var i;i=s(this._maxListeners)?r.defaultMaxListeners:this._maxListeners,i&&i>0&&this._events[t].length>i&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),"function"==typeof console.trace&&console.trace())}return this},r.prototype.on=r.prototype.addListener,r.prototype.once=function(t,e){function r(){this.removeListener(t,r),i||(i=!0,e.apply(this,arguments))}if(!n(e))throw TypeError("listener must be a function");var i=!1;return r.listener=e,this.on(t,r),this},r.prototype.removeListener=function(t,e){var r,i,s,a;if(!n(e))throw TypeError("listener must be a function");if(!this._events||!this._events[t])return this;if(r=this._events[t],s=r.length,i=-1,r===e||n(r.listener)&&r.listener===e)delete this._events[t],this._events.removeListener&&this.emit("removeListener",t,e);else if(o(r)){for(a=s;a-->0;)if(r[a]===e||r[a].listener&&r[a].listener===e){i=a;break}if(0>i)return this;1===r.length?(r.length=0,delete this._events[t]):r.splice(i,1),this._events.removeListener&&this.emit("removeListener",t,e)}return this},r.prototype.removeAllListeners=function(t){var e,r;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[t]&&delete this._events[t],this;if(0===arguments.length){for(e in this._events)"removeListener"!==e&&this.removeAllListeners(e);return this.removeAllListeners("removeListener"),this._events={},this}if(r=this._events[t],n(r))this.removeListener(t,r);else for(;r.length;)this.removeListener(t,r[r.length-1]);return delete this._events[t],this},r.prototype.listeners=function(t){var e;return e=this._events&&this._events[t]?n(this._events[t])?[this._events[t]]:this._events[t].slice():[]},r.listenerCount=function(t,e){var r;return r=t._events&&t._events[e]?n(t._events[e])?1:t._events[e].length:0}},{}]},{},[4])(4)}),"undefined"!=typeof window&&null!==window?window.P=window.Promise:"undefined"!=typeof self&&null!==self&&(self.P=self.Promise);
\ No newline at end of file
--- /dev/null
+"use strict";
+module.exports = function(Promise) {
+var SomePromiseArray = Promise._SomePromiseArray;
+function any(promises) {
+ var ret = new SomePromiseArray(promises);
+ var promise = ret.promise();
+ ret.setHowMany(1);
+ ret.setUnwrap();
+ ret.init();
+ return promise;
+}
+
+Promise.any = function (promises) {
+ return any(promises);
+};
+
+Promise.prototype.any = function () {
+ return any(this);
+};
+
+};
--- /dev/null
+"use strict";
+module.exports = (function(){
+var AssertionError = (function() {
+ function AssertionError(a) {
+ this.constructor$(a);
+ this.message = a;
+ this.name = "AssertionError";
+ }
+ AssertionError.prototype = new Error();
+ AssertionError.prototype.constructor = AssertionError;
+ AssertionError.prototype.constructor$ = Error;
+ return AssertionError;
+})();
+
+function getParams(args) {
+ var params = [];
+ for (var i = 0; i < args.length; ++i) params.push("arg" + i);
+ return params;
+}
+
+function nativeAssert(callName, args, expect) {
+ try {
+ var params = getParams(args);
+ var constructorArgs = params;
+ constructorArgs.push("return " +
+ callName + "("+ params.join(",") + ");");
+ var fn = Function.apply(null, constructorArgs);
+ return fn.apply(null, args);
+ } catch (e) {
+ if (!(e instanceof SyntaxError)) {
+ throw e;
+ } else {
+ return expect;
+ }
+ }
+}
+
+return function assert(boolExpr, message) {
+ if (boolExpr === true) return;
+
+ if (typeof boolExpr === "string" &&
+ boolExpr.charAt(0) === "%") {
+ var nativeCallName = boolExpr;
+ var $_len = arguments.length;var args = new Array($_len - 2); for(var $_i = 2; $_i < $_len; ++$_i) {args[$_i - 2] = arguments[$_i];}
+ if (nativeAssert(nativeCallName, args, message) === message) return;
+ message = (nativeCallName + " !== " + message);
+ }
+
+ var ret = new AssertionError(message);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(ret, assert);
+ }
+ throw ret;
+};
+})();
--- /dev/null
+"use strict";
+var firstLineError;
+try {throw new Error(); } catch (e) {firstLineError = e;}
+var schedule = require("./schedule.js");
+var Queue = require("./queue.js");
+var util = require("./util.js");
+
+function Async() {
+ this._isTickUsed = false;
+ this._lateQueue = new Queue(16);
+ this._normalQueue = new Queue(16);
+ this._trampolineEnabled = true;
+ var self = this;
+ this.drainQueues = function () {
+ self._drainQueues();
+ };
+ this._schedule =
+ schedule.isStatic ? schedule(this.drainQueues) : schedule;
+}
+
+Async.prototype.disableTrampolineIfNecessary = function() {
+ if (util.hasDevTools) {
+ this._trampolineEnabled = false;
+ }
+};
+
+Async.prototype.enableTrampoline = function() {
+ if (!this._trampolineEnabled) {
+ this._trampolineEnabled = true;
+ this._schedule = function(fn) {
+ setTimeout(fn, 0);
+ };
+ }
+};
+
+Async.prototype.haveItemsQueued = function () {
+ return this._normalQueue.length() > 0;
+};
+
+Async.prototype.throwLater = function(fn, arg) {
+ if (arguments.length === 1) {
+ arg = fn;
+ fn = function () { throw arg; };
+ }
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ if (typeof setTimeout !== "undefined") {
+ setTimeout(function() {
+ fn(arg);
+ }, 0);
+ } else try {
+ this._schedule(function() {
+ fn(arg);
+ });
+ } catch (e) {
+ throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/m3OTXk\u000a");
+ }
+};
+
+Async.prototype._getDomain = function() {};
+
+if (!false) {
+if (util.isNode) {
+ var EventsModule = require("events");
+
+ var domainGetter = function() {
+ var domain = process.domain;
+ if (domain === null) return undefined;
+ return domain;
+ };
+
+ if (EventsModule.usingDomains) {
+ Async.prototype._getDomain = domainGetter;
+ } else {
+ var descriptor =
+ Object.getOwnPropertyDescriptor(EventsModule, "usingDomains");
+
+ if (descriptor) {
+ if (!descriptor.configurable) {
+ process.on("domainsActivated", function() {
+ Async.prototype._getDomain = domainGetter;
+ });
+ } else {
+ var usingDomains = false;
+ Object.defineProperty(EventsModule, "usingDomains", {
+ configurable: false,
+ enumerable: true,
+ get: function() {
+ return usingDomains;
+ },
+ set: function(value) {
+ if (usingDomains || !value) return;
+ usingDomains = true;
+ Async.prototype._getDomain = domainGetter;
+ util.toFastProperties(process);
+ process.emit("domainsActivated");
+ }
+ });
+ }
+ }
+ }
+}
+}
+
+function AsyncInvokeLater(fn, receiver, arg) {
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ this._lateQueue.push(fn, receiver, arg);
+ this._queueTick();
+}
+
+function AsyncInvoke(fn, receiver, arg) {
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ this._normalQueue.push(fn, receiver, arg);
+ this._queueTick();
+}
+
+function AsyncSettlePromises(promise) {
+ var domain = this._getDomain();
+ if (domain !== undefined) {
+ var fn = domain.bind(promise._settlePromises);
+ this._normalQueue.push(fn, promise, undefined);
+ } else {
+ this._normalQueue._pushOne(promise);
+ }
+ this._queueTick();
+}
+
+if (!util.hasDevTools) {
+ Async.prototype.invokeLater = AsyncInvokeLater;
+ Async.prototype.invoke = AsyncInvoke;
+ Async.prototype.settlePromises = AsyncSettlePromises;
+} else {
+ Async.prototype.invokeLater = function (fn, receiver, arg) {
+ if (this._trampolineEnabled) {
+ AsyncInvokeLater.call(this, fn, receiver, arg);
+ } else {
+ setTimeout(function() {
+ fn.call(receiver, arg);
+ }, 100);
+ }
+ };
+
+ Async.prototype.invoke = function (fn, receiver, arg) {
+ if (this._trampolineEnabled) {
+ AsyncInvoke.call(this, fn, receiver, arg);
+ } else {
+ setTimeout(function() {
+ fn.call(receiver, arg);
+ }, 0);
+ }
+ };
+
+ Async.prototype.settlePromises = function(promise) {
+ if (this._trampolineEnabled) {
+ AsyncSettlePromises.call(this, promise);
+ } else {
+ setTimeout(function() {
+ promise._settlePromises();
+ }, 0);
+ }
+ };
+}
+
+Async.prototype.invokeFirst = function (fn, receiver, arg) {
+ var domain = this._getDomain();
+ if (domain !== undefined) fn = domain.bind(fn);
+ this._normalQueue.unshift(fn, receiver, arg);
+ this._queueTick();
+};
+
+Async.prototype._drainQueue = function(queue) {
+ while (queue.length() > 0) {
+ var fn = queue.shift();
+ if (typeof fn !== "function") {
+ fn._settlePromises();
+ continue;
+ }
+ var receiver = queue.shift();
+ var arg = queue.shift();
+ fn.call(receiver, arg);
+ }
+};
+
+Async.prototype._drainQueues = function () {
+ this._drainQueue(this._normalQueue);
+ this._reset();
+ this._drainQueue(this._lateQueue);
+};
+
+Async.prototype._queueTick = function () {
+ if (!this._isTickUsed) {
+ this._isTickUsed = true;
+ this._schedule(this.drainQueues);
+ }
+};
+
+Async.prototype._reset = function () {
+ this._isTickUsed = false;
+};
+
+module.exports = new Async();
+module.exports.firstLineError = firstLineError;
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL, tryConvertToPromise) {
+var rejectThis = function(_, e) {
+ this._reject(e);
+};
+
+var targetRejected = function(e, context) {
+ context.promiseRejectionQueued = true;
+ context.bindingPromise._then(rejectThis, rejectThis, null, this, e);
+};
+
+var bindingResolved = function(thisArg, context) {
+ this._setBoundTo(thisArg);
+ if (this._isPending()) {
+ this._resolveCallback(context.target);
+ }
+};
+
+var bindingRejected = function(e, context) {
+ if (!context.promiseRejectionQueued) this._reject(e);
+};
+
+Promise.prototype.bind = function (thisArg) {
+ var maybePromise = tryConvertToPromise(thisArg);
+ var ret = new Promise(INTERNAL);
+ ret._propagateFrom(this, 1);
+ var target = this._target();
+ if (maybePromise instanceof Promise) {
+ var context = {
+ promiseRejectionQueued: false,
+ promise: ret,
+ target: target,
+ bindingPromise: maybePromise
+ };
+ target._then(INTERNAL, targetRejected, ret._progress, ret, context);
+ maybePromise._then(
+ bindingResolved, bindingRejected, ret._progress, ret, context);
+ } else {
+ ret._setBoundTo(thisArg);
+ ret._resolveCallback(target);
+ }
+ return ret;
+};
+
+Promise.prototype._setBoundTo = function (obj) {
+ if (obj !== undefined) {
+ this._bitField = this._bitField | 131072;
+ this._boundTo = obj;
+ } else {
+ this._bitField = this._bitField & (~131072);
+ }
+};
+
+Promise.prototype._isBound = function () {
+ return (this._bitField & 131072) === 131072;
+};
+
+Promise.bind = function (thisArg, value) {
+ var maybePromise = tryConvertToPromise(thisArg);
+ var ret = new Promise(INTERNAL);
+
+ if (maybePromise instanceof Promise) {
+ maybePromise._then(function(thisArg) {
+ ret._setBoundTo(thisArg);
+ ret._resolveCallback(value);
+ }, ret._reject, ret._progress, ret, null);
+ } else {
+ ret._setBoundTo(thisArg);
+ ret._resolveCallback(value);
+ }
+ return ret;
+};
+};
--- /dev/null
+"use strict";
+var old;
+if (typeof Promise !== "undefined") old = Promise;
+function noConflict() {
+ try { if (Promise === bluebird) Promise = old; }
+ catch (e) {}
+ return bluebird;
+}
+var bluebird = require("./promise.js")();
+bluebird.noConflict = noConflict;
+module.exports = bluebird;
--- /dev/null
+"use strict";
+var cr = Object.create;
+if (cr) {
+ var callerCache = cr(null);
+ var getterCache = cr(null);
+ callerCache[" size"] = getterCache[" size"] = 0;
+}
+
+module.exports = function(Promise) {
+var util = require("./util.js");
+var canEvaluate = util.canEvaluate;
+var isIdentifier = util.isIdentifier;
+
+var getMethodCaller;
+var getGetter;
+if (!false) {
+var makeMethodCaller = function (methodName) {
+ return new Function("ensureMethod", " \n\
+ return function(obj) { \n\
+ 'use strict' \n\
+ var len = this.length; \n\
+ ensureMethod(obj, 'methodName'); \n\
+ switch(len) { \n\
+ case 1: return obj.methodName(this[0]); \n\
+ case 2: return obj.methodName(this[0], this[1]); \n\
+ case 3: return obj.methodName(this[0], this[1], this[2]); \n\
+ case 0: return obj.methodName(); \n\
+ default: \n\
+ return obj.methodName.apply(obj, this); \n\
+ } \n\
+ }; \n\
+ ".replace(/methodName/g, methodName))(ensureMethod);
+};
+
+var makeGetter = function (propertyName) {
+ return new Function("obj", " \n\
+ 'use strict'; \n\
+ return obj.propertyName; \n\
+ ".replace("propertyName", propertyName));
+};
+
+var getCompiled = function(name, compiler, cache) {
+ var ret = cache[name];
+ if (typeof ret !== "function") {
+ if (!isIdentifier(name)) {
+ return null;
+ }
+ ret = compiler(name);
+ cache[name] = ret;
+ cache[" size"]++;
+ if (cache[" size"] > 512) {
+ var keys = Object.keys(cache);
+ for (var i = 0; i < 256; ++i) delete cache[keys[i]];
+ cache[" size"] = keys.length - 256;
+ }
+ }
+ return ret;
+};
+
+getMethodCaller = function(name) {
+ return getCompiled(name, makeMethodCaller, callerCache);
+};
+
+getGetter = function(name) {
+ return getCompiled(name, makeGetter, getterCache);
+};
+}
+
+function ensureMethod(obj, methodName) {
+ var fn;
+ if (obj != null) fn = obj[methodName];
+ if (typeof fn !== "function") {
+ var message = "Object " + util.classString(obj) + " has no method '" +
+ util.toString(methodName) + "'";
+ throw new Promise.TypeError(message);
+ }
+ return fn;
+}
+
+function caller(obj) {
+ var methodName = this.pop();
+ var fn = ensureMethod(obj, methodName);
+ return fn.apply(obj, this);
+}
+Promise.prototype.call = function (methodName) {
+ var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];}
+ if (!false) {
+ if (canEvaluate) {
+ var maybeCaller = getMethodCaller(methodName);
+ if (maybeCaller !== null) {
+ return this._then(
+ maybeCaller, undefined, undefined, args, undefined);
+ }
+ }
+ }
+ args.push(methodName);
+ return this._then(caller, undefined, undefined, args, undefined);
+};
+
+function namedGetter(obj) {
+ return obj[this];
+}
+function indexedGetter(obj) {
+ var index = +this;
+ if (index < 0) index = Math.max(0, index + obj.length);
+ return obj[index];
+}
+Promise.prototype.get = function (propertyName) {
+ var isIndex = (typeof propertyName === "number");
+ var getter;
+ if (!isIndex) {
+ if (canEvaluate) {
+ var maybeGetter = getGetter(propertyName);
+ getter = maybeGetter !== null ? maybeGetter : namedGetter;
+ } else {
+ getter = namedGetter;
+ }
+ } else {
+ getter = indexedGetter;
+ }
+ return this._then(getter, undefined, undefined, propertyName, undefined);
+};
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise) {
+var errors = require("./errors.js");
+var async = require("./async.js");
+var CancellationError = errors.CancellationError;
+
+Promise.prototype._cancel = function (reason) {
+ if (!this.isCancellable()) return this;
+ var parent;
+ var promiseToReject = this;
+ while ((parent = promiseToReject._cancellationParent) !== undefined &&
+ parent.isCancellable()) {
+ promiseToReject = parent;
+ }
+ this._unsetCancellable();
+ promiseToReject._target()._rejectCallback(reason, false, true);
+};
+
+Promise.prototype.cancel = function (reason) {
+ if (!this.isCancellable()) return this;
+ if (reason === undefined) reason = new CancellationError();
+ async.invokeLater(this._cancel, this, reason);
+ return this;
+};
+
+Promise.prototype.cancellable = function () {
+ if (this._cancellable()) return this;
+ async.enableTrampoline();
+ this._setCancellable();
+ this._cancellationParent = undefined;
+ return this;
+};
+
+Promise.prototype.uncancellable = function () {
+ var ret = this.then();
+ ret._unsetCancellable();
+ return ret;
+};
+
+Promise.prototype.fork = function (didFulfill, didReject, didProgress) {
+ var ret = this._then(didFulfill, didReject, didProgress,
+ undefined, undefined);
+
+ ret._setCancellable();
+ ret._cancellationParent = undefined;
+ return ret;
+};
+};
--- /dev/null
+"use strict";
+module.exports = function() {
+var async = require("./async.js");
+var util = require("./util.js");
+var bluebirdFramePattern =
+ /[\\\/]bluebird[\\\/]js[\\\/](main|debug|zalgo|instrumented)/;
+var stackFramePattern = null;
+var formatStack = null;
+var indentStackFrames = false;
+var warn;
+
+function CapturedTrace(parent) {
+ this._parent = parent;
+ var length = this._length = 1 + (parent === undefined ? 0 : parent._length);
+ captureStackTrace(this, CapturedTrace);
+ if (length > 32) this.uncycle();
+}
+util.inherits(CapturedTrace, Error);
+
+CapturedTrace.prototype.uncycle = function() {
+ var length = this._length;
+ if (length < 2) return;
+ var nodes = [];
+ var stackToIndex = {};
+
+ for (var i = 0, node = this; node !== undefined; ++i) {
+ nodes.push(node);
+ node = node._parent;
+ }
+ length = this._length = i;
+ for (var i = length - 1; i >= 0; --i) {
+ var stack = nodes[i].stack;
+ if (stackToIndex[stack] === undefined) {
+ stackToIndex[stack] = i;
+ }
+ }
+ for (var i = 0; i < length; ++i) {
+ var currentStack = nodes[i].stack;
+ var index = stackToIndex[currentStack];
+ if (index !== undefined && index !== i) {
+ if (index > 0) {
+ nodes[index - 1]._parent = undefined;
+ nodes[index - 1]._length = 1;
+ }
+ nodes[i]._parent = undefined;
+ nodes[i]._length = 1;
+ var cycleEdgeNode = i > 0 ? nodes[i - 1] : this;
+
+ if (index < length - 1) {
+ cycleEdgeNode._parent = nodes[index + 1];
+ cycleEdgeNode._parent.uncycle();
+ cycleEdgeNode._length =
+ cycleEdgeNode._parent._length + 1;
+ } else {
+ cycleEdgeNode._parent = undefined;
+ cycleEdgeNode._length = 1;
+ }
+ var currentChildLength = cycleEdgeNode._length + 1;
+ for (var j = i - 2; j >= 0; --j) {
+ nodes[j]._length = currentChildLength;
+ currentChildLength++;
+ }
+ return;
+ }
+ }
+};
+
+CapturedTrace.prototype.parent = function() {
+ return this._parent;
+};
+
+CapturedTrace.prototype.hasParent = function() {
+ return this._parent !== undefined;
+};
+
+CapturedTrace.prototype.attachExtraTrace = function(error) {
+ if (error.__stackCleaned__) return;
+ this.uncycle();
+ var parsed = CapturedTrace.parseStackAndMessage(error);
+ var message = parsed.message;
+ var stacks = [parsed.stack];
+
+ var trace = this;
+ while (trace !== undefined) {
+ stacks.push(cleanStack(trace.stack.split("\n")));
+ trace = trace._parent;
+ }
+ removeCommonRoots(stacks);
+ removeDuplicateOrEmptyJumps(stacks);
+ util.notEnumerableProp(error, "stack", reconstructStack(message, stacks));
+ util.notEnumerableProp(error, "__stackCleaned__", true);
+};
+
+function reconstructStack(message, stacks) {
+ for (var i = 0; i < stacks.length - 1; ++i) {
+ stacks[i].push("From previous event:");
+ stacks[i] = stacks[i].join("\n");
+ }
+ if (i < stacks.length) {
+ stacks[i] = stacks[i].join("\n");
+ }
+ return message + "\n" + stacks.join("\n");
+}
+
+function removeDuplicateOrEmptyJumps(stacks) {
+ for (var i = 0; i < stacks.length; ++i) {
+ if (stacks[i].length === 0 ||
+ ((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) {
+ stacks.splice(i, 1);
+ i--;
+ }
+ }
+}
+
+function removeCommonRoots(stacks) {
+ var current = stacks[0];
+ for (var i = 1; i < stacks.length; ++i) {
+ var prev = stacks[i];
+ var currentLastIndex = current.length - 1;
+ var currentLastLine = current[currentLastIndex];
+ var commonRootMeetPoint = -1;
+
+ for (var j = prev.length - 1; j >= 0; --j) {
+ if (prev[j] === currentLastLine) {
+ commonRootMeetPoint = j;
+ break;
+ }
+ }
+
+ for (var j = commonRootMeetPoint; j >= 0; --j) {
+ var line = prev[j];
+ if (current[currentLastIndex] === line) {
+ current.pop();
+ currentLastIndex--;
+ } else {
+ break;
+ }
+ }
+ current = prev;
+ }
+}
+
+function cleanStack(stack) {
+ var ret = [];
+ for (var i = 0; i < stack.length; ++i) {
+ var line = stack[i];
+ var isTraceLine = stackFramePattern.test(line) ||
+ " (No stack trace)" === line;
+ var isInternalFrame = isTraceLine && shouldIgnore(line);
+ if (isTraceLine && !isInternalFrame) {
+ if (indentStackFrames && line.charAt(0) !== " ") {
+ line = " " + line;
+ }
+ ret.push(line);
+ }
+ }
+ return ret;
+}
+
+function stackFramesAsArray(error) {
+ var stack = error.stack.replace(/\s+$/g, "").split("\n");
+ for (var i = 0; i < stack.length; ++i) {
+ var line = stack[i];
+ if (" (No stack trace)" === line || stackFramePattern.test(line)) {
+ break;
+ }
+ }
+ if (i > 0) {
+ stack = stack.slice(i);
+ }
+ return stack;
+}
+
+CapturedTrace.parseStackAndMessage = function(error) {
+ var stack = error.stack;
+ var message = error.toString();
+ stack = typeof stack === "string" && stack.length > 0
+ ? stackFramesAsArray(error) : [" (No stack trace)"];
+ return {
+ message: message,
+ stack: cleanStack(stack)
+ };
+};
+
+CapturedTrace.formatAndLogError = function(error, title) {
+ if (typeof console !== "undefined") {
+ var message;
+ if (typeof error === "object" || typeof error === "function") {
+ var stack = error.stack;
+ message = title + formatStack(stack, error);
+ } else {
+ message = title + String(error);
+ }
+ if (typeof warn === "function") {
+ warn(message);
+ } else if (typeof console.log === "function" ||
+ typeof console.log === "object") {
+ console.log(message);
+ }
+ }
+};
+
+CapturedTrace.unhandledRejection = function (reason) {
+ CapturedTrace.formatAndLogError(reason, "^--- With additional stack trace: ");
+};
+
+CapturedTrace.isSupported = function () {
+ return typeof captureStackTrace === "function";
+};
+
+CapturedTrace.fireRejectionEvent =
+function(name, localHandler, reason, promise) {
+ var localEventFired = false;
+ try {
+ if (typeof localHandler === "function") {
+ localEventFired = true;
+ if (name === "rejectionHandled") {
+ localHandler(promise);
+ } else {
+ localHandler(reason, promise);
+ }
+ }
+ } catch (e) {
+ async.throwLater(e);
+ }
+
+ var globalEventFired = false;
+ try {
+ globalEventFired = fireGlobalEvent(name, reason, promise);
+ } catch (e) {
+ globalEventFired = true;
+ async.throwLater(e);
+ }
+
+ var domEventFired = false;
+ if (fireDomEvent) {
+ try {
+ domEventFired = fireDomEvent(name.toLowerCase(), {
+ reason: reason,
+ promise: promise
+ });
+ } catch (e) {
+ domEventFired = true;
+ async.throwLater(e);
+ }
+ }
+
+ if (!globalEventFired && !localEventFired && !domEventFired &&
+ name === "unhandledRejection") {
+ CapturedTrace.formatAndLogError(reason, "Unhandled rejection ");
+ }
+};
+
+function formatNonError(obj) {
+ var str;
+ if (typeof obj === "function") {
+ str = "[function " +
+ (obj.name || "anonymous") +
+ "]";
+ } else {
+ str = obj.toString();
+ var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/;
+ if (ruselessToString.test(str)) {
+ try {
+ var newStr = JSON.stringify(obj);
+ str = newStr;
+ }
+ catch(e) {
+
+ }
+ }
+ if (str.length === 0) {
+ str = "(empty array)";
+ }
+ }
+ return ("(<" + snip(str) + ">, no stack trace)");
+}
+
+function snip(str) {
+ var maxChars = 41;
+ if (str.length < maxChars) {
+ return str;
+ }
+ return str.substr(0, maxChars - 3) + "...";
+}
+
+var shouldIgnore = function() { return false; };
+var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;
+function parseLineInfo(line) {
+ var matches = line.match(parseLineInfoRegex);
+ if (matches) {
+ return {
+ fileName: matches[1],
+ line: parseInt(matches[2], 10)
+ };
+ }
+}
+CapturedTrace.setBounds = function(firstLineError, lastLineError) {
+ if (!CapturedTrace.isSupported()) return;
+ var firstStackLines = firstLineError.stack.split("\n");
+ var lastStackLines = lastLineError.stack.split("\n");
+ var firstIndex = -1;
+ var lastIndex = -1;
+ var firstFileName;
+ var lastFileName;
+ for (var i = 0; i < firstStackLines.length; ++i) {
+ var result = parseLineInfo(firstStackLines[i]);
+ if (result) {
+ firstFileName = result.fileName;
+ firstIndex = result.line;
+ break;
+ }
+ }
+ for (var i = 0; i < lastStackLines.length; ++i) {
+ var result = parseLineInfo(lastStackLines[i]);
+ if (result) {
+ lastFileName = result.fileName;
+ lastIndex = result.line;
+ break;
+ }
+ }
+ if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName ||
+ firstFileName !== lastFileName || firstIndex >= lastIndex) {
+ return;
+ }
+
+ shouldIgnore = function(line) {
+ if (bluebirdFramePattern.test(line)) return true;
+ var info = parseLineInfo(line);
+ if (info) {
+ if (info.fileName === firstFileName &&
+ (firstIndex <= info.line && info.line <= lastIndex)) {
+ return true;
+ }
+ }
+ return false;
+ };
+};
+
+var captureStackTrace = (function stackDetection() {
+ var v8stackFramePattern = /^\s*at\s*/;
+ var v8stackFormatter = function(stack, error) {
+ if (typeof stack === "string") return stack;
+
+ if (error.name !== undefined &&
+ error.message !== undefined) {
+ return error.toString();
+ }
+ return formatNonError(error);
+ };
+
+ if (typeof Error.stackTraceLimit === "number" &&
+ typeof Error.captureStackTrace === "function") {
+ Error.stackTraceLimit = Error.stackTraceLimit + 6;
+ stackFramePattern = v8stackFramePattern;
+ formatStack = v8stackFormatter;
+ var captureStackTrace = Error.captureStackTrace;
+
+ shouldIgnore = function(line) {
+ return bluebirdFramePattern.test(line);
+ };
+ return function(receiver, ignoreUntil) {
+ Error.stackTraceLimit = Error.stackTraceLimit + 6;
+ captureStackTrace(receiver, ignoreUntil);
+ Error.stackTraceLimit = Error.stackTraceLimit - 6;
+ };
+ }
+ var err = new Error();
+
+ if (typeof err.stack === "string" &&
+ err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) {
+ stackFramePattern = /@/;
+ formatStack = v8stackFormatter;
+ indentStackFrames = true;
+ return function captureStackTrace(o) {
+ o.stack = new Error().stack;
+ };
+ }
+
+ var hasStackAfterThrow;
+ try { throw new Error(); }
+ catch(e) {
+ hasStackAfterThrow = ("stack" in e);
+ }
+ if (!("stack" in err) && hasStackAfterThrow) {
+ stackFramePattern = v8stackFramePattern;
+ formatStack = v8stackFormatter;
+ return function captureStackTrace(o) {
+ Error.stackTraceLimit = Error.stackTraceLimit + 6;
+ try { throw new Error(); }
+ catch(e) { o.stack = e.stack; }
+ Error.stackTraceLimit = Error.stackTraceLimit - 6;
+ };
+ }
+
+ formatStack = function(stack, error) {
+ if (typeof stack === "string") return stack;
+
+ if ((typeof error === "object" ||
+ typeof error === "function") &&
+ error.name !== undefined &&
+ error.message !== undefined) {
+ return error.toString();
+ }
+ return formatNonError(error);
+ };
+
+ return null;
+
+})([]);
+
+var fireDomEvent;
+var fireGlobalEvent = (function() {
+ if (util.isNode) {
+ return function(name, reason, promise) {
+ if (name === "rejectionHandled") {
+ return process.emit(name, promise);
+ } else {
+ return process.emit(name, reason, promise);
+ }
+ };
+ } else {
+ var customEventWorks = false;
+ var anyEventWorks = true;
+ try {
+ var ev = new self.CustomEvent("test");
+ customEventWorks = ev instanceof CustomEvent;
+ } catch (e) {}
+ if (!customEventWorks) {
+ try {
+ var event = document.createEvent("CustomEvent");
+ event.initCustomEvent("testingtheevent", false, true, {});
+ self.dispatchEvent(event);
+ } catch (e) {
+ anyEventWorks = false;
+ }
+ }
+ if (anyEventWorks) {
+ fireDomEvent = function(type, detail) {
+ var event;
+ if (customEventWorks) {
+ event = new self.CustomEvent(type, {
+ detail: detail,
+ bubbles: false,
+ cancelable: true
+ });
+ } else if (self.dispatchEvent) {
+ event = document.createEvent("CustomEvent");
+ event.initCustomEvent(type, false, true, detail);
+ }
+
+ return event ? !self.dispatchEvent(event) : false;
+ };
+ }
+
+ var toWindowMethodNameMap = {};
+ toWindowMethodNameMap["unhandledRejection"] = ("on" +
+ "unhandledRejection").toLowerCase();
+ toWindowMethodNameMap["rejectionHandled"] = ("on" +
+ "rejectionHandled").toLowerCase();
+
+ return function(name, reason, promise) {
+ var methodName = toWindowMethodNameMap[name];
+ var method = self[methodName];
+ if (!method) return false;
+ if (name === "rejectionHandled") {
+ method.call(self, promise);
+ } else {
+ method.call(self, reason, promise);
+ }
+ return true;
+ };
+ }
+})();
+
+if (typeof console !== "undefined" && typeof console.warn !== "undefined") {
+ warn = function (message) {
+ console.warn(message);
+ };
+ if (util.isNode && process.stderr.isTTY) {
+ warn = function(message) {
+ process.stderr.write("\u001b[31m" + message + "\u001b[39m\n");
+ };
+ } else if (!util.isNode && typeof (new Error().stack) === "string") {
+ warn = function(message) {
+ console.warn("%c" + message, "color: red");
+ };
+ }
+}
+
+return CapturedTrace;
+};
--- /dev/null
+"use strict";
+module.exports = function(NEXT_FILTER) {
+var util = require("./util.js");
+var errors = require("./errors.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var keys = require("./es5.js").keys;
+var TypeError = errors.TypeError;
+
+function CatchFilter(instances, callback, promise) {
+ this._instances = instances;
+ this._callback = callback;
+ this._promise = promise;
+}
+
+function safePredicate(predicate, e) {
+ var safeObject = {};
+ var retfilter = tryCatch(predicate).call(safeObject, e);
+
+ if (retfilter === errorObj) return retfilter;
+
+ var safeKeys = keys(safeObject);
+ if (safeKeys.length) {
+ errorObj.e = new TypeError("Catch filter must inherit from Error or be a simple predicate function\u000a\u000a See http://goo.gl/o84o68\u000a");
+ return errorObj;
+ }
+ return retfilter;
+}
+
+CatchFilter.prototype.doFilter = function (e) {
+ var cb = this._callback;
+ var promise = this._promise;
+ var boundTo = promise._boundTo;
+ for (var i = 0, len = this._instances.length; i < len; ++i) {
+ var item = this._instances[i];
+ var itemIsErrorType = item === Error ||
+ (item != null && item.prototype instanceof Error);
+
+ if (itemIsErrorType && e instanceof item) {
+ var ret = tryCatch(cb).call(boundTo, e);
+ if (ret === errorObj) {
+ NEXT_FILTER.e = ret.e;
+ return NEXT_FILTER;
+ }
+ return ret;
+ } else if (typeof item === "function" && !itemIsErrorType) {
+ var shouldHandle = safePredicate(item, e);
+ if (shouldHandle === errorObj) {
+ e = errorObj.e;
+ break;
+ } else if (shouldHandle) {
+ var ret = tryCatch(cb).call(boundTo, e);
+ if (ret === errorObj) {
+ NEXT_FILTER.e = ret.e;
+ return NEXT_FILTER;
+ }
+ return ret;
+ }
+ }
+ }
+ NEXT_FILTER.e = e;
+ return NEXT_FILTER;
+};
+
+return CatchFilter;
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, CapturedTrace, isDebugging) {
+var contextStack = [];
+function Context() {
+ this._trace = new CapturedTrace(peekContext());
+}
+Context.prototype._pushContext = function () {
+ if (!isDebugging()) return;
+ if (this._trace !== undefined) {
+ contextStack.push(this._trace);
+ }
+};
+
+Context.prototype._popContext = function () {
+ if (!isDebugging()) return;
+ if (this._trace !== undefined) {
+ contextStack.pop();
+ }
+};
+
+function createContext() {
+ if (isDebugging()) return new Context();
+}
+
+function peekContext() {
+ var lastIndex = contextStack.length - 1;
+ if (lastIndex >= 0) {
+ return contextStack[lastIndex];
+ }
+ return undefined;
+}
+
+Promise.prototype._peekContext = peekContext;
+Promise.prototype._pushContext = Context.prototype._pushContext;
+Promise.prototype._popContext = Context.prototype._popContext;
+
+return createContext;
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, CapturedTrace) {
+var async = require("./async.js");
+var Warning = require("./errors.js").Warning;
+var util = require("./util.js");
+var canAttachTrace = util.canAttachTrace;
+var unhandledRejectionHandled;
+var possiblyUnhandledRejection;
+var debugging = false || (util.isNode &&
+ (!!process.env["BLUEBIRD_DEBUG"] ||
+ process.env["NODE_ENV"] === "development"));
+
+if (debugging) {
+ async.disableTrampolineIfNecessary();
+}
+
+Promise.prototype._ensurePossibleRejectionHandled = function () {
+ this._setRejectionIsUnhandled();
+ async.invokeLater(this._notifyUnhandledRejection, this, undefined);
+};
+
+Promise.prototype._notifyUnhandledRejectionIsHandled = function () {
+ CapturedTrace.fireRejectionEvent("rejectionHandled",
+ unhandledRejectionHandled, undefined, this);
+};
+
+Promise.prototype._notifyUnhandledRejection = function () {
+ if (this._isRejectionUnhandled()) {
+ var reason = this._getCarriedStackTrace() || this._settledValue;
+ this._setUnhandledRejectionIsNotified();
+ CapturedTrace.fireRejectionEvent("unhandledRejection",
+ possiblyUnhandledRejection, reason, this);
+ }
+};
+
+Promise.prototype._setUnhandledRejectionIsNotified = function () {
+ this._bitField = this._bitField | 524288;
+};
+
+Promise.prototype._unsetUnhandledRejectionIsNotified = function () {
+ this._bitField = this._bitField & (~524288);
+};
+
+Promise.prototype._isUnhandledRejectionNotified = function () {
+ return (this._bitField & 524288) > 0;
+};
+
+Promise.prototype._setRejectionIsUnhandled = function () {
+ this._bitField = this._bitField | 2097152;
+};
+
+Promise.prototype._unsetRejectionIsUnhandled = function () {
+ this._bitField = this._bitField & (~2097152);
+ if (this._isUnhandledRejectionNotified()) {
+ this._unsetUnhandledRejectionIsNotified();
+ this._notifyUnhandledRejectionIsHandled();
+ }
+};
+
+Promise.prototype._isRejectionUnhandled = function () {
+ return (this._bitField & 2097152) > 0;
+};
+
+Promise.prototype._setCarriedStackTrace = function (capturedTrace) {
+ this._bitField = this._bitField | 1048576;
+ this._fulfillmentHandler0 = capturedTrace;
+};
+
+Promise.prototype._isCarryingStackTrace = function () {
+ return (this._bitField & 1048576) > 0;
+};
+
+Promise.prototype._getCarriedStackTrace = function () {
+ return this._isCarryingStackTrace()
+ ? this._fulfillmentHandler0
+ : undefined;
+};
+
+Promise.prototype._captureStackTrace = function () {
+ if (debugging) {
+ this._trace = new CapturedTrace(this._peekContext());
+ }
+ return this;
+};
+
+Promise.prototype._attachExtraTrace = function (error, ignoreSelf) {
+ if (debugging && canAttachTrace(error)) {
+ var trace = this._trace;
+ if (trace !== undefined) {
+ if (ignoreSelf) trace = trace._parent;
+ }
+ if (trace !== undefined) {
+ trace.attachExtraTrace(error);
+ } else if (!error.__stackCleaned__) {
+ var parsed = CapturedTrace.parseStackAndMessage(error);
+ util.notEnumerableProp(error, "stack",
+ parsed.message + "\n" + parsed.stack.join("\n"));
+ util.notEnumerableProp(error, "__stackCleaned__", true);
+ }
+ }
+};
+
+Promise.prototype._warn = function(message) {
+ var warning = new Warning(message);
+ var ctx = this._peekContext();
+ if (ctx) {
+ ctx.attachExtraTrace(warning);
+ } else {
+ var parsed = CapturedTrace.parseStackAndMessage(warning);
+ warning.stack = parsed.message + "\n" + parsed.stack.join("\n");
+ }
+ CapturedTrace.formatAndLogError(warning, "");
+};
+
+Promise.onPossiblyUnhandledRejection = function (fn) {
+ possiblyUnhandledRejection = typeof fn === "function" ? fn : undefined;
+};
+
+Promise.onUnhandledRejectionHandled = function (fn) {
+ unhandledRejectionHandled = typeof fn === "function" ? fn : undefined;
+};
+
+Promise.longStackTraces = function () {
+ if (async.haveItemsQueued() &&
+ debugging === false
+ ) {
+ throw new Error("cannot enable long stack traces after promises have been created\u000a\u000a See http://goo.gl/DT1qyG\u000a");
+ }
+ debugging = CapturedTrace.isSupported();
+ if (debugging) {
+ async.disableTrampolineIfNecessary();
+ }
+};
+
+Promise.hasLongStackTraces = function () {
+ return debugging && CapturedTrace.isSupported();
+};
+
+if (!CapturedTrace.isSupported()) {
+ Promise.longStackTraces = function(){};
+ debugging = false;
+}
+
+return function() {
+ return debugging;
+};
+};
--- /dev/null
+"use strict";
+var util = require("./util.js");
+var isPrimitive = util.isPrimitive;
+var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver;
+
+module.exports = function(Promise) {
+var returner = function () {
+ return this;
+};
+var thrower = function () {
+ throw this;
+};
+
+var wrapper = function (value, action) {
+ if (action === 1) {
+ return function () {
+ throw value;
+ };
+ } else if (action === 2) {
+ return function () {
+ return value;
+ };
+ }
+};
+
+
+Promise.prototype["return"] =
+Promise.prototype.thenReturn = function (value) {
+ if (wrapsPrimitiveReceiver && isPrimitive(value)) {
+ return this._then(
+ wrapper(value, 2),
+ undefined,
+ undefined,
+ undefined,
+ undefined
+ );
+ }
+ return this._then(returner, undefined, undefined, value, undefined);
+};
+
+Promise.prototype["throw"] =
+Promise.prototype.thenThrow = function (reason) {
+ if (wrapsPrimitiveReceiver && isPrimitive(reason)) {
+ return this._then(
+ wrapper(reason, 1),
+ undefined,
+ undefined,
+ undefined,
+ undefined
+ );
+ }
+ return this._then(thrower, undefined, undefined, reason, undefined);
+};
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var PromiseReduce = Promise.reduce;
+
+Promise.prototype.each = function (fn) {
+ return PromiseReduce(this, fn, null, INTERNAL);
+};
+
+Promise.each = function (promises, fn) {
+ return PromiseReduce(promises, fn, null, INTERNAL);
+};
+};
--- /dev/null
+"use strict";
+var es5 = require("./es5.js");
+var Objectfreeze = es5.freeze;
+var util = require("./util.js");
+var inherits = util.inherits;
+var notEnumerableProp = util.notEnumerableProp;
+
+function subError(nameProperty, defaultMessage) {
+ function SubError(message) {
+ if (!(this instanceof SubError)) return new SubError(message);
+ notEnumerableProp(this, "message",
+ typeof message === "string" ? message : defaultMessage);
+ notEnumerableProp(this, "name", nameProperty);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.constructor);
+ } else {
+ Error.call(this);
+ }
+ }
+ inherits(SubError, Error);
+ return SubError;
+}
+
+var _TypeError, _RangeError;
+var Warning = subError("Warning", "warning");
+var CancellationError = subError("CancellationError", "cancellation error");
+var TimeoutError = subError("TimeoutError", "timeout error");
+var AggregateError = subError("AggregateError", "aggregate error");
+try {
+ _TypeError = TypeError;
+ _RangeError = RangeError;
+} catch(e) {
+ _TypeError = subError("TypeError", "type error");
+ _RangeError = subError("RangeError", "range error");
+}
+
+var methods = ("join pop push shift unshift slice filter forEach some " +
+ "every map indexOf lastIndexOf reduce reduceRight sort reverse").split(" ");
+
+for (var i = 0; i < methods.length; ++i) {
+ if (typeof Array.prototype[methods[i]] === "function") {
+ AggregateError.prototype[methods[i]] = Array.prototype[methods[i]];
+ }
+}
+
+es5.defineProperty(AggregateError.prototype, "length", {
+ value: 0,
+ configurable: false,
+ writable: true,
+ enumerable: true
+});
+AggregateError.prototype["isOperational"] = true;
+var level = 0;
+AggregateError.prototype.toString = function() {
+ var indent = Array(level * 4 + 1).join(" ");
+ var ret = "\n" + indent + "AggregateError of:" + "\n";
+ level++;
+ indent = Array(level * 4 + 1).join(" ");
+ for (var i = 0; i < this.length; ++i) {
+ var str = this[i] === this ? "[Circular AggregateError]" : this[i] + "";
+ var lines = str.split("\n");
+ for (var j = 0; j < lines.length; ++j) {
+ lines[j] = indent + lines[j];
+ }
+ str = lines.join("\n");
+ ret += str + "\n";
+ }
+ level--;
+ return ret;
+};
+
+function OperationalError(message) {
+ if (!(this instanceof OperationalError))
+ return new OperationalError(message);
+ notEnumerableProp(this, "name", "OperationalError");
+ notEnumerableProp(this, "message", message);
+ this.cause = message;
+ this["isOperational"] = true;
+
+ if (message instanceof Error) {
+ notEnumerableProp(this, "message", message.message);
+ notEnumerableProp(this, "stack", message.stack);
+ } else if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.constructor);
+ }
+
+}
+inherits(OperationalError, Error);
+
+var errorTypes = Error["__BluebirdErrorTypes__"];
+if (!errorTypes) {
+ errorTypes = Objectfreeze({
+ CancellationError: CancellationError,
+ TimeoutError: TimeoutError,
+ OperationalError: OperationalError,
+ RejectionError: OperationalError,
+ AggregateError: AggregateError
+ });
+ notEnumerableProp(Error, "__BluebirdErrorTypes__", errorTypes);
+}
+
+module.exports = {
+ Error: Error,
+ TypeError: _TypeError,
+ RangeError: _RangeError,
+ CancellationError: errorTypes.CancellationError,
+ OperationalError: errorTypes.OperationalError,
+ TimeoutError: errorTypes.TimeoutError,
+ AggregateError: errorTypes.AggregateError,
+ Warning: Warning
+};
--- /dev/null
+var isES5 = (function(){
+ "use strict";
+ return this === undefined;
+})();
+
+if (isES5) {
+ module.exports = {
+ freeze: Object.freeze,
+ defineProperty: Object.defineProperty,
+ getDescriptor: Object.getOwnPropertyDescriptor,
+ keys: Object.keys,
+ names: Object.getOwnPropertyNames,
+ getPrototypeOf: Object.getPrototypeOf,
+ isArray: Array.isArray,
+ isES5: isES5,
+ propertyIsWritable: function(obj, prop) {
+ var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
+ return !!(!descriptor || descriptor.writable || descriptor.set);
+ }
+ };
+} else {
+ var has = {}.hasOwnProperty;
+ var str = {}.toString;
+ var proto = {}.constructor.prototype;
+
+ var ObjectKeys = function (o) {
+ var ret = [];
+ for (var key in o) {
+ if (has.call(o, key)) {
+ ret.push(key);
+ }
+ }
+ return ret;
+ };
+
+ var ObjectGetDescriptor = function(o, key) {
+ return {value: o[key]};
+ };
+
+ var ObjectDefineProperty = function (o, key, desc) {
+ o[key] = desc.value;
+ return o;
+ };
+
+ var ObjectFreeze = function (obj) {
+ return obj;
+ };
+
+ var ObjectGetPrototypeOf = function (obj) {
+ try {
+ return Object(obj).constructor.prototype;
+ }
+ catch (e) {
+ return proto;
+ }
+ };
+
+ var ArrayIsArray = function (obj) {
+ try {
+ return str.call(obj) === "[object Array]";
+ }
+ catch(e) {
+ return false;
+ }
+ };
+
+ module.exports = {
+ isArray: ArrayIsArray,
+ keys: ObjectKeys,
+ names: ObjectKeys,
+ defineProperty: ObjectDefineProperty,
+ getDescriptor: ObjectGetDescriptor,
+ freeze: ObjectFreeze,
+ getPrototypeOf: ObjectGetPrototypeOf,
+ isES5: isES5,
+ propertyIsWritable: function() {
+ return true;
+ }
+ };
+}
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var PromiseMap = Promise.map;
+
+Promise.prototype.filter = function (fn, options) {
+ return PromiseMap(this, fn, options, INTERNAL);
+};
+
+Promise.filter = function (promises, fn, options) {
+ return PromiseMap(promises, fn, options, INTERNAL);
+};
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, NEXT_FILTER, tryConvertToPromise) {
+var util = require("./util.js");
+var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver;
+var isPrimitive = util.isPrimitive;
+var thrower = util.thrower;
+
+function returnThis() {
+ return this;
+}
+function throwThis() {
+ throw this;
+}
+function return$(r) {
+ return function() {
+ return r;
+ };
+}
+function throw$(r) {
+ return function() {
+ throw r;
+ };
+}
+function promisedFinally(ret, reasonOrValue, isFulfilled) {
+ var then;
+ if (wrapsPrimitiveReceiver && isPrimitive(reasonOrValue)) {
+ then = isFulfilled ? return$(reasonOrValue) : throw$(reasonOrValue);
+ } else {
+ then = isFulfilled ? returnThis : throwThis;
+ }
+ return ret._then(then, thrower, undefined, reasonOrValue, undefined);
+}
+
+function finallyHandler(reasonOrValue) {
+ var promise = this.promise;
+ var handler = this.handler;
+
+ var ret = promise._isBound()
+ ? handler.call(promise._boundTo)
+ : handler();
+
+ if (ret !== undefined) {
+ var maybePromise = tryConvertToPromise(ret, promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ return promisedFinally(maybePromise, reasonOrValue,
+ promise.isFulfilled());
+ }
+ }
+
+ if (promise.isRejected()) {
+ NEXT_FILTER.e = reasonOrValue;
+ return NEXT_FILTER;
+ } else {
+ return reasonOrValue;
+ }
+}
+
+function tapHandler(value) {
+ var promise = this.promise;
+ var handler = this.handler;
+
+ var ret = promise._isBound()
+ ? handler.call(promise._boundTo, value)
+ : handler(value);
+
+ if (ret !== undefined) {
+ var maybePromise = tryConvertToPromise(ret, promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ return promisedFinally(maybePromise, value, true);
+ }
+ }
+ return value;
+}
+
+Promise.prototype._passThroughHandler = function (handler, isFinally) {
+ if (typeof handler !== "function") return this.then();
+
+ var promiseAndHandler = {
+ promise: this,
+ handler: handler
+ };
+
+ return this._then(
+ isFinally ? finallyHandler : tapHandler,
+ isFinally ? finallyHandler : undefined, undefined,
+ promiseAndHandler, undefined);
+};
+
+Promise.prototype.lastly =
+Promise.prototype["finally"] = function (handler) {
+ return this._passThroughHandler(handler, true);
+};
+
+Promise.prototype.tap = function (handler) {
+ return this._passThroughHandler(handler, false);
+};
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise,
+ apiRejection,
+ INTERNAL,
+ tryConvertToPromise) {
+var errors = require("./errors.js");
+var TypeError = errors.TypeError;
+var util = require("./util.js");
+var errorObj = util.errorObj;
+var tryCatch = util.tryCatch;
+var yieldHandlers = [];
+
+function promiseFromYieldHandler(value, yieldHandlers, traceParent) {
+ for (var i = 0; i < yieldHandlers.length; ++i) {
+ traceParent._pushContext();
+ var result = tryCatch(yieldHandlers[i])(value);
+ traceParent._popContext();
+ if (result === errorObj) {
+ traceParent._pushContext();
+ var ret = Promise.reject(errorObj.e);
+ traceParent._popContext();
+ return ret;
+ }
+ var maybePromise = tryConvertToPromise(result, traceParent);
+ if (maybePromise instanceof Promise) return maybePromise;
+ }
+ return null;
+}
+
+function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {
+ var promise = this._promise = new Promise(INTERNAL);
+ promise._captureStackTrace();
+ this._stack = stack;
+ this._generatorFunction = generatorFunction;
+ this._receiver = receiver;
+ this._generator = undefined;
+ this._yieldHandlers = typeof yieldHandler === "function"
+ ? [yieldHandler].concat(yieldHandlers)
+ : yieldHandlers;
+}
+
+PromiseSpawn.prototype.promise = function () {
+ return this._promise;
+};
+
+PromiseSpawn.prototype._run = function () {
+ this._generator = this._generatorFunction.call(this._receiver);
+ this._receiver =
+ this._generatorFunction = undefined;
+ this._next(undefined);
+};
+
+PromiseSpawn.prototype._continue = function (result) {
+ if (result === errorObj) {
+ return this._promise._rejectCallback(result.e, false, true);
+ }
+
+ var value = result.value;
+ if (result.done === true) {
+ this._promise._resolveCallback(value);
+ } else {
+ var maybePromise = tryConvertToPromise(value, this._promise);
+ if (!(maybePromise instanceof Promise)) {
+ maybePromise =
+ promiseFromYieldHandler(maybePromise,
+ this._yieldHandlers,
+ this._promise);
+ if (maybePromise === null) {
+ this._throw(
+ new TypeError(
+ "A value %s was yielded that could not be treated as a promise\u000a\u000a See http://goo.gl/4Y4pDk\u000a\u000a".replace("%s", value) +
+ "From coroutine:\u000a" +
+ this._stack.split("\n").slice(1, -7).join("\n")
+ )
+ );
+ return;
+ }
+ }
+ maybePromise._then(
+ this._next,
+ this._throw,
+ undefined,
+ this,
+ null
+ );
+ }
+};
+
+PromiseSpawn.prototype._throw = function (reason) {
+ this._promise._attachExtraTrace(reason);
+ this._promise._pushContext();
+ var result = tryCatch(this._generator["throw"])
+ .call(this._generator, reason);
+ this._promise._popContext();
+ this._continue(result);
+};
+
+PromiseSpawn.prototype._next = function (value) {
+ this._promise._pushContext();
+ var result = tryCatch(this._generator.next).call(this._generator, value);
+ this._promise._popContext();
+ this._continue(result);
+};
+
+Promise.coroutine = function (generatorFunction, options) {
+ if (typeof generatorFunction !== "function") {
+ throw new TypeError("generatorFunction must be a function\u000a\u000a See http://goo.gl/6Vqhm0\u000a");
+ }
+ var yieldHandler = Object(options).yieldHandler;
+ var PromiseSpawn$ = PromiseSpawn;
+ var stack = new Error().stack;
+ return function () {
+ var generator = generatorFunction.apply(this, arguments);
+ var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
+ stack);
+ spawn._generator = generator;
+ spawn._next(undefined);
+ return spawn.promise();
+ };
+};
+
+Promise.coroutine.addYieldHandler = function(fn) {
+ if (typeof fn !== "function") throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ yieldHandlers.push(fn);
+};
+
+Promise.spawn = function (generatorFunction) {
+ if (typeof generatorFunction !== "function") {
+ return apiRejection("generatorFunction must be a function\u000a\u000a See http://goo.gl/6Vqhm0\u000a");
+ }
+ var spawn = new PromiseSpawn(generatorFunction, this);
+ var ret = spawn.promise();
+ spawn._run(Promise.spawn);
+ return ret;
+};
+};
--- /dev/null
+"use strict";
+module.exports =
+function(Promise, PromiseArray, tryConvertToPromise, INTERNAL) {
+var util = require("./util.js");
+var canEvaluate = util.canEvaluate;
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var reject;
+
+if (!false) {
+if (canEvaluate) {
+ var thenCallback = function(i) {
+ return new Function("value", "holder", " \n\
+ 'use strict'; \n\
+ holder.pIndex = value; \n\
+ holder.checkFulfillment(this); \n\
+ ".replace(/Index/g, i));
+ };
+
+ var caller = function(count) {
+ var values = [];
+ for (var i = 1; i <= count; ++i) values.push("holder.p" + i);
+ return new Function("holder", " \n\
+ 'use strict'; \n\
+ var callback = holder.fn; \n\
+ return callback(values); \n\
+ ".replace(/values/g, values.join(", ")));
+ };
+ var thenCallbacks = [];
+ var callers = [undefined];
+ for (var i = 1; i <= 5; ++i) {
+ thenCallbacks.push(thenCallback(i));
+ callers.push(caller(i));
+ }
+
+ var Holder = function(total, fn) {
+ this.p1 = this.p2 = this.p3 = this.p4 = this.p5 = null;
+ this.fn = fn;
+ this.total = total;
+ this.now = 0;
+ };
+
+ Holder.prototype.callers = callers;
+ Holder.prototype.checkFulfillment = function(promise) {
+ var now = this.now;
+ now++;
+ var total = this.total;
+ if (now >= total) {
+ var handler = this.callers[total];
+ promise._pushContext();
+ var ret = tryCatch(handler)(this);
+ promise._popContext();
+ if (ret === errorObj) {
+ promise._rejectCallback(ret.e, false, true);
+ } else {
+ promise._resolveCallback(ret);
+ }
+ } else {
+ this.now = now;
+ }
+ };
+
+ var reject = function (reason) {
+ this._reject(reason);
+ };
+}
+}
+
+Promise.join = function () {
+ var last = arguments.length - 1;
+ var fn;
+ if (last > 0 && typeof arguments[last] === "function") {
+ fn = arguments[last];
+ if (!false) {
+ if (last < 6 && canEvaluate) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ var holder = new Holder(last, fn);
+ var callbacks = thenCallbacks;
+ for (var i = 0; i < last; ++i) {
+ var maybePromise = tryConvertToPromise(arguments[i], ret);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ maybePromise._then(callbacks[i], reject,
+ undefined, ret, holder);
+ } else if (maybePromise._isFulfilled()) {
+ callbacks[i].call(ret,
+ maybePromise._value(), holder);
+ } else {
+ ret._reject(maybePromise._reason());
+ }
+ } else {
+ callbacks[i].call(ret, maybePromise, holder);
+ }
+ }
+ return ret;
+ }
+ }
+ }
+ var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];}
+ if (fn) args.pop();
+ var ret = new PromiseArray(args).promise();
+ return fn !== undefined ? ret.spread(fn) : ret;
+};
+
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise,
+ PromiseArray,
+ apiRejection,
+ tryConvertToPromise,
+ INTERNAL) {
+var async = require("./async.js");
+var util = require("./util.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var PENDING = {};
+var EMPTY_ARRAY = [];
+
+function MappingPromiseArray(promises, fn, limit, _filter) {
+ this.constructor$(promises);
+ this._promise._captureStackTrace();
+ this._callback = fn;
+ this._preservedValues = _filter === INTERNAL
+ ? new Array(this.length())
+ : null;
+ this._limit = limit;
+ this._inFlight = 0;
+ this._queue = limit >= 1 ? [] : EMPTY_ARRAY;
+ async.invoke(init, this, undefined);
+}
+util.inherits(MappingPromiseArray, PromiseArray);
+function init() {this._init$(undefined, -2);}
+
+MappingPromiseArray.prototype._init = function () {};
+
+MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ var values = this._values;
+ var length = this.length();
+ var preservedValues = this._preservedValues;
+ var limit = this._limit;
+ if (values[index] === PENDING) {
+ values[index] = value;
+ if (limit >= 1) {
+ this._inFlight--;
+ this._drainQueue();
+ if (this._isResolved()) return;
+ }
+ } else {
+ if (limit >= 1 && this._inFlight >= limit) {
+ values[index] = value;
+ this._queue.push(index);
+ return;
+ }
+ if (preservedValues !== null) preservedValues[index] = value;
+
+ var callback = this._callback;
+ var receiver = this._promise._boundTo;
+ this._promise._pushContext();
+ var ret = tryCatch(callback).call(receiver, value, index, length);
+ this._promise._popContext();
+ if (ret === errorObj) return this._reject(ret.e);
+
+ var maybePromise = tryConvertToPromise(ret, this._promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ if (limit >= 1) this._inFlight++;
+ values[index] = PENDING;
+ return maybePromise._proxyPromiseArray(this, index);
+ } else if (maybePromise._isFulfilled()) {
+ ret = maybePromise._value();
+ } else {
+ return this._reject(maybePromise._reason());
+ }
+ }
+ values[index] = ret;
+ }
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= length) {
+ if (preservedValues !== null) {
+ this._filter(values, preservedValues);
+ } else {
+ this._resolve(values);
+ }
+
+ }
+};
+
+MappingPromiseArray.prototype._drainQueue = function () {
+ var queue = this._queue;
+ var limit = this._limit;
+ var values = this._values;
+ while (queue.length > 0 && this._inFlight < limit) {
+ if (this._isResolved()) return;
+ var index = queue.pop();
+ this._promiseFulfilled(values[index], index);
+ }
+};
+
+MappingPromiseArray.prototype._filter = function (booleans, values) {
+ var len = values.length;
+ var ret = new Array(len);
+ var j = 0;
+ for (var i = 0; i < len; ++i) {
+ if (booleans[i]) ret[j++] = values[i];
+ }
+ ret.length = j;
+ this._resolve(ret);
+};
+
+MappingPromiseArray.prototype.preservedValues = function () {
+ return this._preservedValues;
+};
+
+function map(promises, fn, options, _filter) {
+ var limit = typeof options === "object" && options !== null
+ ? options.concurrency
+ : 0;
+ limit = typeof limit === "number" &&
+ isFinite(limit) && limit >= 1 ? limit : 0;
+ return new MappingPromiseArray(promises, fn, limit, _filter);
+}
+
+Promise.prototype.map = function (fn, options) {
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+
+ return map(this, fn, options, null).promise();
+};
+
+Promise.map = function (promises, fn, options, _filter) {
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ return map(promises, fn, options, _filter).promise();
+};
+
+
+};
--- /dev/null
+"use strict";
+module.exports =
+function(Promise, INTERNAL, tryConvertToPromise, apiRejection) {
+var util = require("./util.js");
+var tryCatch = util.tryCatch;
+
+Promise.method = function (fn) {
+ if (typeof fn !== "function") {
+ throw new Promise.TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ }
+ return function () {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._pushContext();
+ var value = tryCatch(fn).apply(this, arguments);
+ ret._popContext();
+ ret._resolveFromSyncValue(value);
+ return ret;
+ };
+};
+
+Promise.attempt = Promise["try"] = function (fn, args, ctx) {
+ if (typeof fn !== "function") {
+ return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ }
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._pushContext();
+ var value = util.isArray(args)
+ ? tryCatch(fn).apply(ctx, args)
+ : tryCatch(fn).call(ctx, args);
+ ret._popContext();
+ ret._resolveFromSyncValue(value);
+ return ret;
+};
+
+Promise.prototype._resolveFromSyncValue = function (value) {
+ if (value === util.errorObj) {
+ this._rejectCallback(value.e, false, true);
+ } else {
+ this._resolveCallback(value, true);
+ }
+};
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise) {
+var util = require("./util.js");
+var async = require("./async.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+
+function spreadAdapter(val, nodeback) {
+ var promise = this;
+ if (!util.isArray(val)) return successAdapter.call(promise, val, nodeback);
+ var ret = tryCatch(nodeback).apply(promise._boundTo, [null].concat(val));
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+
+function successAdapter(val, nodeback) {
+ var promise = this;
+ var receiver = promise._boundTo;
+ var ret = val === undefined
+ ? tryCatch(nodeback).call(receiver, null)
+ : tryCatch(nodeback).call(receiver, null, val);
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+function errorAdapter(reason, nodeback) {
+ var promise = this;
+ if (!reason) {
+ var target = promise._target();
+ var newReason = target._getCarriedStackTrace();
+ newReason.cause = reason;
+ reason = newReason;
+ }
+ var ret = tryCatch(nodeback).call(promise._boundTo, reason);
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+
+Promise.prototype.asCallback =
+Promise.prototype.nodeify = function (nodeback, options) {
+ if (typeof nodeback == "function") {
+ var adapter = successAdapter;
+ if (options !== undefined && Object(options).spread) {
+ adapter = spreadAdapter;
+ }
+ this._then(
+ adapter,
+ errorAdapter,
+ undefined,
+ this,
+ nodeback
+ );
+ }
+ return this;
+};
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, PromiseArray) {
+var util = require("./util.js");
+var async = require("./async.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+
+Promise.prototype.progressed = function (handler) {
+ return this._then(undefined, undefined, handler, undefined, undefined);
+};
+
+Promise.prototype._progress = function (progressValue) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ this._target()._progressUnchecked(progressValue);
+
+};
+
+Promise.prototype._progressHandlerAt = function (index) {
+ return index === 0
+ ? this._progressHandler0
+ : this[(index << 2) + index - 5 + 2];
+};
+
+Promise.prototype._doProgressWith = function (progression) {
+ var progressValue = progression.value;
+ var handler = progression.handler;
+ var promise = progression.promise;
+ var receiver = progression.receiver;
+
+ var ret = tryCatch(handler).call(receiver, progressValue);
+ if (ret === errorObj) {
+ if (ret.e != null &&
+ ret.e.name !== "StopProgressPropagation") {
+ var trace = util.canAttachTrace(ret.e)
+ ? ret.e : new Error(util.toString(ret.e));
+ promise._attachExtraTrace(trace);
+ promise._progress(ret.e);
+ }
+ } else if (ret instanceof Promise) {
+ ret._then(promise._progress, null, null, promise, undefined);
+ } else {
+ promise._progress(ret);
+ }
+};
+
+
+Promise.prototype._progressUnchecked = function (progressValue) {
+ var len = this._length();
+ var progress = this._progress;
+ for (var i = 0; i < len; i++) {
+ var handler = this._progressHandlerAt(i);
+ var promise = this._promiseAt(i);
+ if (!(promise instanceof Promise)) {
+ var receiver = this._receiverAt(i);
+ if (typeof handler === "function") {
+ handler.call(receiver, progressValue, promise);
+ } else if (receiver instanceof PromiseArray &&
+ !receiver._isResolved()) {
+ receiver._promiseProgressed(progressValue, promise);
+ }
+ continue;
+ }
+
+ if (typeof handler === "function") {
+ async.invoke(this._doProgressWith, this, {
+ handler: handler,
+ promise: promise,
+ receiver: this._receiverAt(i),
+ value: progressValue
+ });
+ } else {
+ async.invoke(progress, promise, progressValue);
+ }
+ }
+};
+};
--- /dev/null
+"use strict";
+module.exports = function() {
+var makeSelfResolutionError = function () {
+ return new TypeError("circular promise resolution chain\u000a\u000a See http://goo.gl/LhFpo0\u000a");
+};
+var reflect = function() {
+ return new Promise.PromiseInspection(this._target());
+};
+var apiRejection = function(msg) {
+ return Promise.reject(new TypeError(msg));
+};
+var util = require("./util.js");
+var async = require("./async.js");
+var errors = require("./errors.js");
+var TypeError = Promise.TypeError = errors.TypeError;
+Promise.RangeError = errors.RangeError;
+Promise.CancellationError = errors.CancellationError;
+Promise.TimeoutError = errors.TimeoutError;
+Promise.OperationalError = errors.OperationalError;
+Promise.RejectionError = errors.OperationalError;
+Promise.AggregateError = errors.AggregateError;
+var INTERNAL = function(){};
+var APPLY = {};
+var NEXT_FILTER = {e: null};
+var tryConvertToPromise = require("./thenables.js")(Promise, INTERNAL);
+var PromiseArray =
+ require("./promise_array.js")(Promise, INTERNAL,
+ tryConvertToPromise, apiRejection);
+var CapturedTrace = require("./captured_trace.js")();
+var isDebugging = require("./debuggability.js")(Promise, CapturedTrace);
+ /*jshint unused:false*/
+var createContext =
+ require("./context.js")(Promise, CapturedTrace, isDebugging);
+var CatchFilter = require("./catch_filter.js")(NEXT_FILTER);
+var PromiseResolver = require("./promise_resolver.js");
+var nodebackForPromise = PromiseResolver._nodebackForPromise;
+var errorObj = util.errorObj;
+var tryCatch = util.tryCatch;
+function Promise(resolver) {
+ if (typeof resolver !== "function") {
+ throw new TypeError("the promise constructor requires a resolver function\u000a\u000a See http://goo.gl/EC22Yn\u000a");
+ }
+ if (this.constructor !== Promise) {
+ throw new TypeError("the promise constructor cannot be invoked directly\u000a\u000a See http://goo.gl/KsIlge\u000a");
+ }
+ this._bitField = 0;
+ this._fulfillmentHandler0 = undefined;
+ this._rejectionHandler0 = undefined;
+ this._progressHandler0 = undefined;
+ this._promise0 = undefined;
+ this._receiver0 = undefined;
+ this._settledValue = undefined;
+ if (resolver !== INTERNAL) this._resolveFromResolver(resolver);
+}
+
+Promise.prototype.toString = function () {
+ return "[object Promise]";
+};
+
+Promise.prototype.caught = Promise.prototype["catch"] = function (fn) {
+ var len = arguments.length;
+ if (len > 1) {
+ var catchInstances = new Array(len - 1),
+ j = 0, i;
+ for (i = 0; i < len - 1; ++i) {
+ var item = arguments[i];
+ if (typeof item === "function") {
+ catchInstances[j++] = item;
+ } else {
+ return Promise.reject(
+ new TypeError("Catch filter must inherit from Error or be a simple predicate function\u000a\u000a See http://goo.gl/o84o68\u000a"));
+ }
+ }
+ catchInstances.length = j;
+ fn = arguments[i];
+ var catchFilter = new CatchFilter(catchInstances, fn, this);
+ return this._then(undefined, catchFilter.doFilter, undefined,
+ catchFilter, undefined);
+ }
+ return this._then(undefined, fn, undefined, undefined, undefined);
+};
+
+Promise.prototype.reflect = function () {
+ return this._then(reflect, reflect, undefined, this, undefined);
+};
+
+Promise.prototype.then = function (didFulfill, didReject, didProgress) {
+ if (isDebugging() && arguments.length > 0 &&
+ typeof didFulfill !== "function" &&
+ typeof didReject !== "function") {
+ var msg = ".then() only accepts functions but was passed: " +
+ util.classString(didFulfill);
+ if (arguments.length > 1) {
+ msg += ", " + util.classString(didReject);
+ }
+ this._warn(msg);
+ }
+ return this._then(didFulfill, didReject, didProgress,
+ undefined, undefined);
+};
+
+Promise.prototype.done = function (didFulfill, didReject, didProgress) {
+ var promise = this._then(didFulfill, didReject, didProgress,
+ undefined, undefined);
+ promise._setIsFinal();
+};
+
+Promise.prototype.spread = function (didFulfill, didReject) {
+ return this.all()._then(didFulfill, didReject, undefined, APPLY, undefined);
+};
+
+Promise.prototype.isCancellable = function () {
+ return !this.isResolved() &&
+ this._cancellable();
+};
+
+Promise.prototype.toJSON = function () {
+ var ret = {
+ isFulfilled: false,
+ isRejected: false,
+ fulfillmentValue: undefined,
+ rejectionReason: undefined
+ };
+ if (this.isFulfilled()) {
+ ret.fulfillmentValue = this.value();
+ ret.isFulfilled = true;
+ } else if (this.isRejected()) {
+ ret.rejectionReason = this.reason();
+ ret.isRejected = true;
+ }
+ return ret;
+};
+
+Promise.prototype.all = function () {
+ return new PromiseArray(this).promise();
+};
+
+Promise.prototype.error = function (fn) {
+ return this.caught(util.originatesFromRejection, fn);
+};
+
+Promise.is = function (val) {
+ return val instanceof Promise;
+};
+
+Promise.fromNode = function(fn) {
+ var ret = new Promise(INTERNAL);
+ var result = tryCatch(fn)(nodebackForPromise(ret));
+ if (result === errorObj) {
+ ret._rejectCallback(result.e, true, true);
+ }
+ return ret;
+};
+
+Promise.all = function (promises) {
+ return new PromiseArray(promises).promise();
+};
+
+Promise.defer = Promise.pending = function () {
+ var promise = new Promise(INTERNAL);
+ return new PromiseResolver(promise);
+};
+
+Promise.cast = function (obj) {
+ var ret = tryConvertToPromise(obj);
+ if (!(ret instanceof Promise)) {
+ var val = ret;
+ ret = new Promise(INTERNAL);
+ ret._fulfillUnchecked(val);
+ }
+ return ret;
+};
+
+Promise.resolve = Promise.fulfilled = Promise.cast;
+
+Promise.reject = Promise.rejected = function (reason) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._rejectCallback(reason, true);
+ return ret;
+};
+
+Promise.setScheduler = function(fn) {
+ if (typeof fn !== "function") throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ var prev = async._schedule;
+ async._schedule = fn;
+ return prev;
+};
+
+Promise.prototype._then = function (
+ didFulfill,
+ didReject,
+ didProgress,
+ receiver,
+ internalData
+) {
+ var haveInternalData = internalData !== undefined;
+ var ret = haveInternalData ? internalData : new Promise(INTERNAL);
+
+ if (!haveInternalData) {
+ ret._propagateFrom(this, 4 | 1);
+ ret._captureStackTrace();
+ }
+
+ var target = this._target();
+ if (target !== this) {
+ if (receiver === undefined) receiver = this._boundTo;
+ if (!haveInternalData) ret._setIsMigrated();
+ }
+
+ var callbackIndex =
+ target._addCallbacks(didFulfill, didReject, didProgress, ret, receiver);
+
+ if (target._isResolved() && !target._isSettlePromisesQueued()) {
+ async.invoke(
+ target._settlePromiseAtPostResolution, target, callbackIndex);
+ }
+
+ return ret;
+};
+
+Promise.prototype._settlePromiseAtPostResolution = function (index) {
+ if (this._isRejectionUnhandled()) this._unsetRejectionIsUnhandled();
+ this._settlePromiseAt(index);
+};
+
+Promise.prototype._length = function () {
+ return this._bitField & 131071;
+};
+
+Promise.prototype._isFollowingOrFulfilledOrRejected = function () {
+ return (this._bitField & 939524096) > 0;
+};
+
+Promise.prototype._isFollowing = function () {
+ return (this._bitField & 536870912) === 536870912;
+};
+
+Promise.prototype._setLength = function (len) {
+ this._bitField = (this._bitField & -131072) |
+ (len & 131071);
+};
+
+Promise.prototype._setFulfilled = function () {
+ this._bitField = this._bitField | 268435456;
+};
+
+Promise.prototype._setRejected = function () {
+ this._bitField = this._bitField | 134217728;
+};
+
+Promise.prototype._setFollowing = function () {
+ this._bitField = this._bitField | 536870912;
+};
+
+Promise.prototype._setIsFinal = function () {
+ this._bitField = this._bitField | 33554432;
+};
+
+Promise.prototype._isFinal = function () {
+ return (this._bitField & 33554432) > 0;
+};
+
+Promise.prototype._cancellable = function () {
+ return (this._bitField & 67108864) > 0;
+};
+
+Promise.prototype._setCancellable = function () {
+ this._bitField = this._bitField | 67108864;
+};
+
+Promise.prototype._unsetCancellable = function () {
+ this._bitField = this._bitField & (~67108864);
+};
+
+Promise.prototype._setIsMigrated = function () {
+ this._bitField = this._bitField | 4194304;
+};
+
+Promise.prototype._unsetIsMigrated = function () {
+ this._bitField = this._bitField & (~4194304);
+};
+
+Promise.prototype._isMigrated = function () {
+ return (this._bitField & 4194304) > 0;
+};
+
+Promise.prototype._receiverAt = function (index) {
+ var ret = index === 0
+ ? this._receiver0
+ : this[
+ index * 5 - 5 + 4];
+ if (ret === undefined && this._isBound()) {
+ return this._boundTo;
+ }
+ return ret;
+};
+
+Promise.prototype._promiseAt = function (index) {
+ return index === 0
+ ? this._promise0
+ : this[index * 5 - 5 + 3];
+};
+
+Promise.prototype._fulfillmentHandlerAt = function (index) {
+ return index === 0
+ ? this._fulfillmentHandler0
+ : this[index * 5 - 5 + 0];
+};
+
+Promise.prototype._rejectionHandlerAt = function (index) {
+ return index === 0
+ ? this._rejectionHandler0
+ : this[index * 5 - 5 + 1];
+};
+
+Promise.prototype._migrateCallbacks = function (follower, index) {
+ var fulfill = follower._fulfillmentHandlerAt(index);
+ var reject = follower._rejectionHandlerAt(index);
+ var progress = follower._progressHandlerAt(index);
+ var promise = follower._promiseAt(index);
+ var receiver = follower._receiverAt(index);
+ if (promise instanceof Promise) promise._setIsMigrated();
+ this._addCallbacks(fulfill, reject, progress, promise, receiver);
+};
+
+Promise.prototype._addCallbacks = function (
+ fulfill,
+ reject,
+ progress,
+ promise,
+ receiver
+) {
+ var index = this._length();
+
+ if (index >= 131071 - 5) {
+ index = 0;
+ this._setLength(0);
+ }
+
+ if (index === 0) {
+ this._promise0 = promise;
+ if (receiver !== undefined) this._receiver0 = receiver;
+ if (typeof fulfill === "function" && !this._isCarryingStackTrace())
+ this._fulfillmentHandler0 = fulfill;
+ if (typeof reject === "function") this._rejectionHandler0 = reject;
+ if (typeof progress === "function") this._progressHandler0 = progress;
+ } else {
+ var base = index * 5 - 5;
+ this[base + 3] = promise;
+ this[base + 4] = receiver;
+ if (typeof fulfill === "function")
+ this[base + 0] = fulfill;
+ if (typeof reject === "function")
+ this[base + 1] = reject;
+ if (typeof progress === "function")
+ this[base + 2] = progress;
+ }
+ this._setLength(index + 1);
+ return index;
+};
+
+Promise.prototype._setProxyHandlers = function (receiver, promiseSlotValue) {
+ var index = this._length();
+
+ if (index >= 131071 - 5) {
+ index = 0;
+ this._setLength(0);
+ }
+ if (index === 0) {
+ this._promise0 = promiseSlotValue;
+ this._receiver0 = receiver;
+ } else {
+ var base = index * 5 - 5;
+ this[base + 3] = promiseSlotValue;
+ this[base + 4] = receiver;
+ }
+ this._setLength(index + 1);
+};
+
+Promise.prototype._proxyPromiseArray = function (promiseArray, index) {
+ this._setProxyHandlers(promiseArray, index);
+};
+
+Promise.prototype._resolveCallback = function(value, shouldBind) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ if (value === this)
+ return this._rejectCallback(makeSelfResolutionError(), false, true);
+ var maybePromise = tryConvertToPromise(value, this);
+ if (!(maybePromise instanceof Promise)) return this._fulfill(value);
+
+ var propagationFlags = 1 | (shouldBind ? 4 : 0);
+ this._propagateFrom(maybePromise, propagationFlags);
+ var promise = maybePromise._target();
+ if (promise._isPending()) {
+ var len = this._length();
+ for (var i = 0; i < len; ++i) {
+ promise._migrateCallbacks(this, i);
+ }
+ this._setFollowing();
+ this._setLength(0);
+ this._setFollowee(promise);
+ } else if (promise._isFulfilled()) {
+ this._fulfillUnchecked(promise._value());
+ } else {
+ this._rejectUnchecked(promise._reason(),
+ promise._getCarriedStackTrace());
+ }
+};
+
+Promise.prototype._rejectCallback =
+function(reason, synchronous, shouldNotMarkOriginatingFromRejection) {
+ if (!shouldNotMarkOriginatingFromRejection) {
+ util.markAsOriginatingFromRejection(reason);
+ }
+ var trace = util.ensureErrorObject(reason);
+ var hasStack = trace === reason;
+ this._attachExtraTrace(trace, synchronous ? hasStack : false);
+ this._reject(reason, hasStack ? undefined : trace);
+};
+
+Promise.prototype._resolveFromResolver = function (resolver) {
+ var promise = this;
+ this._captureStackTrace();
+ this._pushContext();
+ var synchronous = true;
+ var r = tryCatch(resolver)(function(value) {
+ if (promise === null) return;
+ promise._resolveCallback(value);
+ promise = null;
+ }, function (reason) {
+ if (promise === null) return;
+ promise._rejectCallback(reason, synchronous);
+ promise = null;
+ });
+ synchronous = false;
+ this._popContext();
+
+ if (r !== undefined && r === errorObj && promise !== null) {
+ promise._rejectCallback(r.e, true, true);
+ promise = null;
+ }
+};
+
+Promise.prototype._settlePromiseFromHandler = function (
+ handler, receiver, value, promise
+) {
+ if (promise._isRejected()) return;
+ promise._pushContext();
+ var x;
+ if (receiver === APPLY && !this._isRejected()) {
+ x = tryCatch(handler).apply(this._boundTo, value);
+ } else {
+ x = tryCatch(handler).call(receiver, value);
+ }
+ promise._popContext();
+
+ if (x === errorObj || x === promise || x === NEXT_FILTER) {
+ var err = x === promise ? makeSelfResolutionError() : x.e;
+ promise._rejectCallback(err, false, true);
+ } else {
+ promise._resolveCallback(x);
+ }
+};
+
+Promise.prototype._target = function() {
+ var ret = this;
+ while (ret._isFollowing()) ret = ret._followee();
+ return ret;
+};
+
+Promise.prototype._followee = function() {
+ return this._rejectionHandler0;
+};
+
+Promise.prototype._setFollowee = function(promise) {
+ this._rejectionHandler0 = promise;
+};
+
+Promise.prototype._cleanValues = function () {
+ if (this._cancellable()) {
+ this._cancellationParent = undefined;
+ }
+};
+
+Promise.prototype._propagateFrom = function (parent, flags) {
+ if ((flags & 1) > 0 && parent._cancellable()) {
+ this._setCancellable();
+ this._cancellationParent = parent;
+ }
+ if ((flags & 4) > 0 && parent._isBound()) {
+ this._setBoundTo(parent._boundTo);
+ }
+};
+
+Promise.prototype._fulfill = function (value) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ this._fulfillUnchecked(value);
+};
+
+Promise.prototype._reject = function (reason, carriedStackTrace) {
+ if (this._isFollowingOrFulfilledOrRejected()) return;
+ this._rejectUnchecked(reason, carriedStackTrace);
+};
+
+Promise.prototype._settlePromiseAt = function (index) {
+ var promise = this._promiseAt(index);
+ var isPromise = promise instanceof Promise;
+
+ if (isPromise && promise._isMigrated()) {
+ promise._unsetIsMigrated();
+ return async.invoke(this._settlePromiseAt, this, index);
+ }
+ var handler = this._isFulfilled()
+ ? this._fulfillmentHandlerAt(index)
+ : this._rejectionHandlerAt(index);
+
+ var carriedStackTrace =
+ this._isCarryingStackTrace() ? this._getCarriedStackTrace() : undefined;
+ var value = this._settledValue;
+ var receiver = this._receiverAt(index);
+
+
+ this._clearCallbackDataAtIndex(index);
+
+ if (typeof handler === "function") {
+ if (!isPromise) {
+ handler.call(receiver, value, promise);
+ } else {
+ this._settlePromiseFromHandler(handler, receiver, value, promise);
+ }
+ } else if (receiver instanceof PromiseArray) {
+ if (!receiver._isResolved()) {
+ if (this._isFulfilled()) {
+ receiver._promiseFulfilled(value, promise);
+ }
+ else {
+ receiver._promiseRejected(value, promise);
+ }
+ }
+ } else if (isPromise) {
+ if (this._isFulfilled()) {
+ promise._fulfill(value);
+ } else {
+ promise._reject(value, carriedStackTrace);
+ }
+ }
+
+ if (index >= 4 && (index & 31) === 4)
+ async.invokeLater(this._setLength, this, 0);
+};
+
+Promise.prototype._clearCallbackDataAtIndex = function(index) {
+ if (index === 0) {
+ if (!this._isCarryingStackTrace()) {
+ this._fulfillmentHandler0 = undefined;
+ }
+ this._rejectionHandler0 =
+ this._progressHandler0 =
+ this._receiver0 =
+ this._promise0 = undefined;
+ } else {
+ var base = index * 5 - 5;
+ this[base + 3] =
+ this[base + 4] =
+ this[base + 0] =
+ this[base + 1] =
+ this[base + 2] = undefined;
+ }
+};
+
+Promise.prototype._isSettlePromisesQueued = function () {
+ return (this._bitField &
+ -1073741824) === -1073741824;
+};
+
+Promise.prototype._setSettlePromisesQueued = function () {
+ this._bitField = this._bitField | -1073741824;
+};
+
+Promise.prototype._unsetSettlePromisesQueued = function () {
+ this._bitField = this._bitField & (~-1073741824);
+};
+
+Promise.prototype._queueSettlePromises = function() {
+ async.settlePromises(this);
+ this._setSettlePromisesQueued();
+};
+
+Promise.prototype._fulfillUnchecked = function (value) {
+ if (value === this) {
+ var err = makeSelfResolutionError();
+ this._attachExtraTrace(err);
+ return this._rejectUnchecked(err, undefined);
+ }
+ this._setFulfilled();
+ this._settledValue = value;
+ this._cleanValues();
+
+ if (this._length() > 0) {
+ this._queueSettlePromises();
+ }
+};
+
+Promise.prototype._rejectUncheckedCheckError = function (reason) {
+ var trace = util.ensureErrorObject(reason);
+ this._rejectUnchecked(reason, trace === reason ? undefined : trace);
+};
+
+Promise.prototype._rejectUnchecked = function (reason, trace) {
+ if (reason === this) {
+ var err = makeSelfResolutionError();
+ this._attachExtraTrace(err);
+ return this._rejectUnchecked(err);
+ }
+ this._setRejected();
+ this._settledValue = reason;
+ this._cleanValues();
+
+ if (this._isFinal()) {
+ async.throwLater(function(e) {
+ if ("stack" in e) {
+ async.invokeFirst(
+ CapturedTrace.unhandledRejection, undefined, e);
+ }
+ throw e;
+ }, trace === undefined ? reason : trace);
+ return;
+ }
+
+ if (trace !== undefined && trace !== reason) {
+ this._setCarriedStackTrace(trace);
+ }
+
+ if (this._length() > 0) {
+ this._queueSettlePromises();
+ } else {
+ this._ensurePossibleRejectionHandled();
+ }
+};
+
+Promise.prototype._settlePromises = function () {
+ this._unsetSettlePromisesQueued();
+ var len = this._length();
+ for (var i = 0; i < len; i++) {
+ this._settlePromiseAt(i);
+ }
+};
+
+Promise._makeSelfResolutionError = makeSelfResolutionError;
+require("./progress.js")(Promise, PromiseArray);
+require("./method.js")(Promise, INTERNAL, tryConvertToPromise, apiRejection);
+require("./bind.js")(Promise, INTERNAL, tryConvertToPromise);
+require("./finally.js")(Promise, NEXT_FILTER, tryConvertToPromise);
+require("./direct_resolve.js")(Promise);
+require("./synchronous_inspection.js")(Promise);
+require("./join.js")(Promise, PromiseArray, tryConvertToPromise, INTERNAL);
+Promise.Promise = Promise;
+require('./map.js')(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL);
+require('./cancel.js')(Promise);
+require('./using.js')(Promise, apiRejection, tryConvertToPromise, createContext);
+require('./generators.js')(Promise, apiRejection, INTERNAL, tryConvertToPromise);
+require('./nodeify.js')(Promise);
+require('./call_get.js')(Promise);
+require('./props.js')(Promise, PromiseArray, tryConvertToPromise, apiRejection);
+require('./race.js')(Promise, INTERNAL, tryConvertToPromise, apiRejection);
+require('./reduce.js')(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL);
+require('./settle.js')(Promise, PromiseArray);
+require('./some.js')(Promise, PromiseArray, apiRejection);
+require('./promisify.js')(Promise, INTERNAL);
+require('./any.js')(Promise);
+require('./each.js')(Promise, INTERNAL);
+require('./timers.js')(Promise, INTERNAL);
+require('./filter.js')(Promise, INTERNAL);
+
+ util.toFastProperties(Promise);
+ util.toFastProperties(Promise.prototype);
+ function fillTypes(value) {
+ var p = new Promise(INTERNAL);
+ p._fulfillmentHandler0 = value;
+ p._rejectionHandler0 = value;
+ p._progressHandler0 = value;
+ p._promise0 = value;
+ p._receiver0 = value;
+ p._settledValue = value;
+ }
+ // Complete slack tracking, opt out of field-type tracking and
+ // stabilize map
+ fillTypes({a: 1});
+ fillTypes({b: 2});
+ fillTypes({c: 3});
+ fillTypes(1);
+ fillTypes(function(){});
+ fillTypes(undefined);
+ fillTypes(false);
+ fillTypes(new Promise(INTERNAL));
+ CapturedTrace.setBounds(async.firstLineError, util.lastLineError);
+ return Promise;
+
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL, tryConvertToPromise,
+ apiRejection) {
+var util = require("./util.js");
+var isArray = util.isArray;
+
+function toResolutionValue(val) {
+ switch(val) {
+ case -2: return [];
+ case -3: return {};
+ }
+}
+
+function PromiseArray(values) {
+ var promise = this._promise = new Promise(INTERNAL);
+ var parent;
+ if (values instanceof Promise) {
+ parent = values;
+ promise._propagateFrom(parent, 1 | 4);
+ }
+ this._values = values;
+ this._length = 0;
+ this._totalResolved = 0;
+ this._init(undefined, -2);
+}
+PromiseArray.prototype.length = function () {
+ return this._length;
+};
+
+PromiseArray.prototype.promise = function () {
+ return this._promise;
+};
+
+PromiseArray.prototype._init = function init(_, resolveValueIfEmpty) {
+ var values = tryConvertToPromise(this._values, this._promise);
+ if (values instanceof Promise) {
+ values = values._target();
+ this._values = values;
+ if (values._isFulfilled()) {
+ values = values._value();
+ if (!isArray(values)) {
+ var err = new Promise.TypeError("expecting an array, a promise or a thenable\u000a\u000a See http://goo.gl/s8MMhc\u000a");
+ this.__hardReject__(err);
+ return;
+ }
+ } else if (values._isPending()) {
+ values._then(
+ init,
+ this._reject,
+ undefined,
+ this,
+ resolveValueIfEmpty
+ );
+ return;
+ } else {
+ this._reject(values._reason());
+ return;
+ }
+ } else if (!isArray(values)) {
+ this._promise._reject(apiRejection("expecting an array, a promise or a thenable\u000a\u000a See http://goo.gl/s8MMhc\u000a")._reason());
+ return;
+ }
+
+ if (values.length === 0) {
+ if (resolveValueIfEmpty === -5) {
+ this._resolveEmptyArray();
+ }
+ else {
+ this._resolve(toResolutionValue(resolveValueIfEmpty));
+ }
+ return;
+ }
+ var len = this.getActualLength(values.length);
+ this._length = len;
+ this._values = this.shouldCopyValues() ? new Array(len) : this._values;
+ var promise = this._promise;
+ for (var i = 0; i < len; ++i) {
+ var isResolved = this._isResolved();
+ var maybePromise = tryConvertToPromise(values[i], promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (isResolved) {
+ maybePromise._unsetRejectionIsUnhandled();
+ } else if (maybePromise._isPending()) {
+ maybePromise._proxyPromiseArray(this, i);
+ } else if (maybePromise._isFulfilled()) {
+ this._promiseFulfilled(maybePromise._value(), i);
+ } else {
+ this._promiseRejected(maybePromise._reason(), i);
+ }
+ } else if (!isResolved) {
+ this._promiseFulfilled(maybePromise, i);
+ }
+ }
+};
+
+PromiseArray.prototype._isResolved = function () {
+ return this._values === null;
+};
+
+PromiseArray.prototype._resolve = function (value) {
+ this._values = null;
+ this._promise._fulfill(value);
+};
+
+PromiseArray.prototype.__hardReject__ =
+PromiseArray.prototype._reject = function (reason) {
+ this._values = null;
+ this._promise._rejectCallback(reason, false, true);
+};
+
+PromiseArray.prototype._promiseProgressed = function (progressValue, index) {
+ this._promise._progress({
+ index: index,
+ value: progressValue
+ });
+};
+
+
+PromiseArray.prototype._promiseFulfilled = function (value, index) {
+ this._values[index] = value;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ this._resolve(this._values);
+ }
+};
+
+PromiseArray.prototype._promiseRejected = function (reason, index) {
+ this._totalResolved++;
+ this._reject(reason);
+};
+
+PromiseArray.prototype.shouldCopyValues = function () {
+ return true;
+};
+
+PromiseArray.prototype.getActualLength = function (len) {
+ return len;
+};
+
+return PromiseArray;
+};
--- /dev/null
+"use strict";
+var util = require("./util.js");
+var maybeWrapAsError = util.maybeWrapAsError;
+var errors = require("./errors.js");
+var TimeoutError = errors.TimeoutError;
+var OperationalError = errors.OperationalError;
+var haveGetters = util.haveGetters;
+var es5 = require("./es5.js");
+
+function isUntypedError(obj) {
+ return obj instanceof Error &&
+ es5.getPrototypeOf(obj) === Error.prototype;
+}
+
+var rErrorKey = /^(?:name|message|stack|cause)$/;
+function wrapAsOperationalError(obj) {
+ var ret;
+ if (isUntypedError(obj)) {
+ ret = new OperationalError(obj);
+ ret.name = obj.name;
+ ret.message = obj.message;
+ ret.stack = obj.stack;
+ var keys = es5.keys(obj);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (!rErrorKey.test(key)) {
+ ret[key] = obj[key];
+ }
+ }
+ return ret;
+ }
+ util.markAsOriginatingFromRejection(obj);
+ return obj;
+}
+
+function nodebackForPromise(promise) {
+ return function(err, value) {
+ if (promise === null) return;
+
+ if (err) {
+ var wrapped = wrapAsOperationalError(maybeWrapAsError(err));
+ promise._attachExtraTrace(wrapped);
+ promise._reject(wrapped);
+ } else if (arguments.length > 2) {
+ var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];}
+ promise._fulfill(args);
+ } else {
+ promise._fulfill(value);
+ }
+
+ promise = null;
+ };
+}
+
+
+var PromiseResolver;
+if (!haveGetters) {
+ PromiseResolver = function (promise) {
+ this.promise = promise;
+ this.asCallback = nodebackForPromise(promise);
+ this.callback = this.asCallback;
+ };
+}
+else {
+ PromiseResolver = function (promise) {
+ this.promise = promise;
+ };
+}
+if (haveGetters) {
+ var prop = {
+ get: function() {
+ return nodebackForPromise(this.promise);
+ }
+ };
+ es5.defineProperty(PromiseResolver.prototype, "asCallback", prop);
+ es5.defineProperty(PromiseResolver.prototype, "callback", prop);
+}
+
+PromiseResolver._nodebackForPromise = nodebackForPromise;
+
+PromiseResolver.prototype.toString = function () {
+ return "[object PromiseResolver]";
+};
+
+PromiseResolver.prototype.resolve =
+PromiseResolver.prototype.fulfill = function (value) {
+ if (!(this instanceof PromiseResolver)) {
+ throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\u000a\u000a See http://goo.gl/sdkXL9\u000a");
+ }
+ this.promise._resolveCallback(value);
+};
+
+PromiseResolver.prototype.reject = function (reason) {
+ if (!(this instanceof PromiseResolver)) {
+ throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\u000a\u000a See http://goo.gl/sdkXL9\u000a");
+ }
+ this.promise._rejectCallback(reason);
+};
+
+PromiseResolver.prototype.progress = function (value) {
+ if (!(this instanceof PromiseResolver)) {
+ throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\u000a\u000a See http://goo.gl/sdkXL9\u000a");
+ }
+ this.promise._progress(value);
+};
+
+PromiseResolver.prototype.cancel = function (err) {
+ this.promise.cancel(err);
+};
+
+PromiseResolver.prototype.timeout = function () {
+ this.reject(new TimeoutError("timeout"));
+};
+
+PromiseResolver.prototype.isResolved = function () {
+ return this.promise.isResolved();
+};
+
+PromiseResolver.prototype.toJSON = function () {
+ return this.promise.toJSON();
+};
+
+module.exports = PromiseResolver;
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var THIS = {};
+var util = require("./util.js");
+var nodebackForPromise = require("./promise_resolver.js")
+ ._nodebackForPromise;
+var withAppended = util.withAppended;
+var maybeWrapAsError = util.maybeWrapAsError;
+var canEvaluate = util.canEvaluate;
+var TypeError = require("./errors").TypeError;
+var defaultSuffix = "Async";
+var defaultPromisified = {__isPromisified__: true};
+var noCopyPropsPattern =
+ /^(?:length|name|arguments|caller|callee|prototype|__isPromisified__)$/;
+var defaultFilter = function(name, func) {
+ return util.isIdentifier(name) &&
+ name.charAt(0) !== "_" &&
+ !util.isClass(func);
+};
+
+function propsFilter(key) {
+ return !noCopyPropsPattern.test(key);
+}
+
+function isPromisified(fn) {
+ try {
+ return fn.__isPromisified__ === true;
+ }
+ catch (e) {
+ return false;
+ }
+}
+
+function hasPromisified(obj, key, suffix) {
+ var val = util.getDataPropertyOrDefault(obj, key + suffix,
+ defaultPromisified);
+ return val ? isPromisified(val) : false;
+}
+function checkValid(ret, suffix, suffixRegexp) {
+ for (var i = 0; i < ret.length; i += 2) {
+ var key = ret[i];
+ if (suffixRegexp.test(key)) {
+ var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
+ for (var j = 0; j < ret.length; j += 2) {
+ if (ret[j] === keyWithoutAsyncSuffix) {
+ throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a See http://goo.gl/iWrZbw\u000a"
+ .replace("%s", suffix));
+ }
+ }
+ }
+ }
+}
+
+function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
+ var keys = util.inheritedDataKeys(obj);
+ var ret = [];
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ var value = obj[key];
+ var passesDefaultFilter = filter === defaultFilter
+ ? true : defaultFilter(key, value, obj);
+ if (typeof value === "function" &&
+ !isPromisified(value) &&
+ !hasPromisified(obj, key, suffix) &&
+ filter(key, value, obj, passesDefaultFilter)) {
+ ret.push(key, value);
+ }
+ }
+ checkValid(ret, suffix, suffixRegexp);
+ return ret;
+}
+
+var escapeIdentRegex = function(str) {
+ return str.replace(/([$])/, "\\$");
+};
+
+var makeNodePromisifiedEval;
+if (!false) {
+var switchCaseArgumentOrder = function(likelyArgumentCount) {
+ var ret = [likelyArgumentCount];
+ var min = Math.max(0, likelyArgumentCount - 1 - 3);
+ for(var i = likelyArgumentCount - 1; i >= min; --i) {
+ ret.push(i);
+ }
+ for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
+ ret.push(i);
+ }
+ return ret;
+};
+
+var argumentSequence = function(argumentCount) {
+ return util.filledRange(argumentCount, "_arg", "");
+};
+
+var parameterDeclaration = function(parameterCount) {
+ return util.filledRange(
+ Math.max(parameterCount, 3), "_arg", "");
+};
+
+var parameterCount = function(fn) {
+ if (typeof fn.length === "number") {
+ return Math.max(Math.min(fn.length, 1023 + 1), 0);
+ }
+ return 0;
+};
+
+makeNodePromisifiedEval =
+function(callback, receiver, originalName, fn) {
+ var newParameterCount = Math.max(0, parameterCount(fn) - 1);
+ var argumentOrder = switchCaseArgumentOrder(newParameterCount);
+ var shouldProxyThis = typeof callback === "string" || receiver === THIS;
+
+ function generateCallForArgumentCount(count) {
+ var args = argumentSequence(count).join(", ");
+ var comma = count > 0 ? ", " : "";
+ var ret;
+ if (shouldProxyThis) {
+ ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
+ } else {
+ ret = receiver === undefined
+ ? "ret = callback({{args}}, nodeback); break;\n"
+ : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
+ }
+ return ret.replace("{{args}}", args).replace(", ", comma);
+ }
+
+ function generateArgumentSwitchCase() {
+ var ret = "";
+ for (var i = 0; i < argumentOrder.length; ++i) {
+ ret += "case " + argumentOrder[i] +":" +
+ generateCallForArgumentCount(argumentOrder[i]);
+ }
+
+ ret += " \n\
+ default: \n\
+ var args = new Array(len + 1); \n\
+ var i = 0; \n\
+ for (var i = 0; i < len; ++i) { \n\
+ args[i] = arguments[i]; \n\
+ } \n\
+ args[i] = nodeback; \n\
+ [CodeForCall] \n\
+ break; \n\
+ ".replace("[CodeForCall]", (shouldProxyThis
+ ? "ret = callback.apply(this, args);\n"
+ : "ret = callback.apply(receiver, args);\n"));
+ return ret;
+ }
+
+ var getFunctionCode = typeof callback === "string"
+ ? ("this != null ? this['"+callback+"'] : fn")
+ : "fn";
+
+ return new Function("Promise",
+ "fn",
+ "receiver",
+ "withAppended",
+ "maybeWrapAsError",
+ "nodebackForPromise",
+ "tryCatch",
+ "errorObj",
+ "INTERNAL","'use strict'; \n\
+ var ret = function (Parameters) { \n\
+ 'use strict'; \n\
+ var len = arguments.length; \n\
+ var promise = new Promise(INTERNAL); \n\
+ promise._captureStackTrace(); \n\
+ var nodeback = nodebackForPromise(promise); \n\
+ var ret; \n\
+ var callback = tryCatch([GetFunctionCode]); \n\
+ switch(len) { \n\
+ [CodeForSwitchCase] \n\
+ } \n\
+ if (ret === errorObj) { \n\
+ promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
+ } \n\
+ return promise; \n\
+ }; \n\
+ ret.__isPromisified__ = true; \n\
+ return ret; \n\
+ "
+ .replace("Parameters", parameterDeclaration(newParameterCount))
+ .replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
+ .replace("[GetFunctionCode]", getFunctionCode))(
+ Promise,
+ fn,
+ receiver,
+ withAppended,
+ maybeWrapAsError,
+ nodebackForPromise,
+ util.tryCatch,
+ util.errorObj,
+ INTERNAL
+ );
+};
+}
+
+function makeNodePromisifiedClosure(callback, receiver, _, fn) {
+ var defaultThis = (function() {return this;})();
+ var method = callback;
+ if (typeof method === "string") {
+ callback = fn;
+ }
+ function promisified() {
+ var _receiver = receiver;
+ if (receiver === THIS) _receiver = this;
+ var promise = new Promise(INTERNAL);
+ promise._captureStackTrace();
+ var cb = typeof method === "string" && this !== defaultThis
+ ? this[method] : callback;
+ var fn = nodebackForPromise(promise);
+ try {
+ cb.apply(_receiver, withAppended(arguments, fn));
+ } catch(e) {
+ promise._rejectCallback(maybeWrapAsError(e), true, true);
+ }
+ return promise;
+ }
+ promisified.__isPromisified__ = true;
+ return promisified;
+}
+
+var makeNodePromisified = canEvaluate
+ ? makeNodePromisifiedEval
+ : makeNodePromisifiedClosure;
+
+function promisifyAll(obj, suffix, filter, promisifier) {
+ var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
+ var methods =
+ promisifiableMethods(obj, suffix, suffixRegexp, filter);
+
+ for (var i = 0, len = methods.length; i < len; i+= 2) {
+ var key = methods[i];
+ var fn = methods[i+1];
+ var promisifiedKey = key + suffix;
+ obj[promisifiedKey] = promisifier === makeNodePromisified
+ ? makeNodePromisified(key, THIS, key, fn, suffix)
+ : promisifier(fn, function() {
+ return makeNodePromisified(key, THIS, key, fn, suffix);
+ });
+ }
+ util.toFastProperties(obj);
+ return obj;
+}
+
+function promisify(callback, receiver) {
+ return makeNodePromisified(callback, receiver, undefined, callback);
+}
+
+Promise.promisify = function (fn, receiver) {
+ if (typeof fn !== "function") {
+ throw new TypeError("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ }
+ if (isPromisified(fn)) {
+ return fn;
+ }
+ var ret = promisify(fn, arguments.length < 2 ? THIS : receiver);
+ util.copyDescriptors(fn, ret, propsFilter);
+ return ret;
+};
+
+Promise.promisifyAll = function (target, options) {
+ if (typeof target !== "function" && typeof target !== "object") {
+ throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a See http://goo.gl/9ITlV0\u000a");
+ }
+ options = Object(options);
+ var suffix = options.suffix;
+ if (typeof suffix !== "string") suffix = defaultSuffix;
+ var filter = options.filter;
+ if (typeof filter !== "function") filter = defaultFilter;
+ var promisifier = options.promisifier;
+ if (typeof promisifier !== "function") promisifier = makeNodePromisified;
+
+ if (!util.isIdentifier(suffix)) {
+ throw new RangeError("suffix must be a valid identifier\u000a\u000a See http://goo.gl/8FZo5V\u000a");
+ }
+
+ var keys = util.inheritedDataKeys(target);
+ for (var i = 0; i < keys.length; ++i) {
+ var value = target[keys[i]];
+ if (keys[i] !== "constructor" &&
+ util.isClass(value)) {
+ promisifyAll(value.prototype, suffix, filter, promisifier);
+ promisifyAll(value, suffix, filter, promisifier);
+ }
+ }
+
+ return promisifyAll(target, suffix, filter, promisifier);
+};
+};
+
--- /dev/null
+"use strict";
+module.exports = function(
+ Promise, PromiseArray, tryConvertToPromise, apiRejection) {
+var util = require("./util.js");
+var isObject = util.isObject;
+var es5 = require("./es5.js");
+
+function PropertiesPromiseArray(obj) {
+ var keys = es5.keys(obj);
+ var len = keys.length;
+ var values = new Array(len * 2);
+ for (var i = 0; i < len; ++i) {
+ var key = keys[i];
+ values[i] = obj[key];
+ values[i + len] = key;
+ }
+ this.constructor$(values);
+}
+util.inherits(PropertiesPromiseArray, PromiseArray);
+
+PropertiesPromiseArray.prototype._init = function () {
+ this._init$(undefined, -3) ;
+};
+
+PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ this._values[index] = value;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ var val = {};
+ var keyOffset = this.length();
+ for (var i = 0, len = this.length(); i < len; ++i) {
+ val[this._values[i + keyOffset]] = this._values[i];
+ }
+ this._resolve(val);
+ }
+};
+
+PropertiesPromiseArray.prototype._promiseProgressed = function (value, index) {
+ this._promise._progress({
+ key: this._values[index + this.length()],
+ value: value
+ });
+};
+
+PropertiesPromiseArray.prototype.shouldCopyValues = function () {
+ return false;
+};
+
+PropertiesPromiseArray.prototype.getActualLength = function (len) {
+ return len >> 1;
+};
+
+function props(promises) {
+ var ret;
+ var castValue = tryConvertToPromise(promises);
+
+ if (!isObject(castValue)) {
+ return apiRejection("cannot await properties of a non-object\u000a\u000a See http://goo.gl/OsFKC8\u000a");
+ } else if (castValue instanceof Promise) {
+ ret = castValue._then(
+ Promise.props, undefined, undefined, undefined, undefined);
+ } else {
+ ret = new PropertiesPromiseArray(castValue).promise();
+ }
+
+ if (castValue instanceof Promise) {
+ ret._propagateFrom(castValue, 4);
+ }
+ return ret;
+}
+
+Promise.prototype.props = function () {
+ return props(this);
+};
+
+Promise.props = function (promises) {
+ return props(promises);
+};
+};
--- /dev/null
+"use strict";
+function arrayMove(src, srcIndex, dst, dstIndex, len) {
+ for (var j = 0; j < len; ++j) {
+ dst[j + dstIndex] = src[j + srcIndex];
+ src[j + srcIndex] = void 0;
+ }
+}
+
+function Queue(capacity) {
+ this._capacity = capacity;
+ this._length = 0;
+ this._front = 0;
+}
+
+Queue.prototype._willBeOverCapacity = function (size) {
+ return this._capacity < size;
+};
+
+Queue.prototype._pushOne = function (arg) {
+ var length = this.length();
+ this._checkCapacity(length + 1);
+ var i = (this._front + length) & (this._capacity - 1);
+ this[i] = arg;
+ this._length = length + 1;
+};
+
+Queue.prototype._unshiftOne = function(value) {
+ var capacity = this._capacity;
+ this._checkCapacity(this.length() + 1);
+ var front = this._front;
+ var i = (((( front - 1 ) &
+ ( capacity - 1) ) ^ capacity ) - capacity );
+ this[i] = value;
+ this._front = i;
+ this._length = this.length() + 1;
+};
+
+Queue.prototype.unshift = function(fn, receiver, arg) {
+ this._unshiftOne(arg);
+ this._unshiftOne(receiver);
+ this._unshiftOne(fn);
+};
+
+Queue.prototype.push = function (fn, receiver, arg) {
+ var length = this.length() + 3;
+ if (this._willBeOverCapacity(length)) {
+ this._pushOne(fn);
+ this._pushOne(receiver);
+ this._pushOne(arg);
+ return;
+ }
+ var j = this._front + length - 3;
+ this._checkCapacity(length);
+ var wrapMask = this._capacity - 1;
+ this[(j + 0) & wrapMask] = fn;
+ this[(j + 1) & wrapMask] = receiver;
+ this[(j + 2) & wrapMask] = arg;
+ this._length = length;
+};
+
+Queue.prototype.shift = function () {
+ var front = this._front,
+ ret = this[front];
+
+ this[front] = undefined;
+ this._front = (front + 1) & (this._capacity - 1);
+ this._length--;
+ return ret;
+};
+
+Queue.prototype.length = function () {
+ return this._length;
+};
+
+Queue.prototype._checkCapacity = function (size) {
+ if (this._capacity < size) {
+ this._resizeTo(this._capacity << 1);
+ }
+};
+
+Queue.prototype._resizeTo = function (capacity) {
+ var oldCapacity = this._capacity;
+ this._capacity = capacity;
+ var front = this._front;
+ var length = this._length;
+ var moveItemsCount = (front + length) & (oldCapacity - 1);
+ arrayMove(this, 0, this, oldCapacity, moveItemsCount);
+};
+
+module.exports = Queue;
--- /dev/null
+"use strict";
+module.exports = function(
+ Promise, INTERNAL, tryConvertToPromise, apiRejection) {
+var isArray = require("./util.js").isArray;
+
+var raceLater = function (promise) {
+ return promise.then(function(array) {
+ return race(array, promise);
+ });
+};
+
+function race(promises, parent) {
+ var maybePromise = tryConvertToPromise(promises);
+
+ if (maybePromise instanceof Promise) {
+ return raceLater(maybePromise);
+ } else if (!isArray(promises)) {
+ return apiRejection("expecting an array, a promise or a thenable\u000a\u000a See http://goo.gl/s8MMhc\u000a");
+ }
+
+ var ret = new Promise(INTERNAL);
+ if (parent !== undefined) {
+ ret._propagateFrom(parent, 4 | 1);
+ }
+ var fulfill = ret._fulfill;
+ var reject = ret._reject;
+ for (var i = 0, len = promises.length; i < len; ++i) {
+ var val = promises[i];
+
+ if (val === undefined && !(i in promises)) {
+ continue;
+ }
+
+ Promise.cast(val)._then(fulfill, reject, undefined, ret, null);
+ }
+ return ret;
+}
+
+Promise.race = function (promises) {
+ return race(promises, undefined);
+};
+
+Promise.prototype.race = function () {
+ return race(this, undefined);
+};
+
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise,
+ PromiseArray,
+ apiRejection,
+ tryConvertToPromise,
+ INTERNAL) {
+var async = require("./async.js");
+var util = require("./util.js");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+function ReductionPromiseArray(promises, fn, accum, _each) {
+ this.constructor$(promises);
+ this._promise._captureStackTrace();
+ this._preservedValues = _each === INTERNAL ? [] : null;
+ this._zerothIsAccum = (accum === undefined);
+ this._gotAccum = false;
+ this._reducingIndex = (this._zerothIsAccum ? 1 : 0);
+ this._valuesPhase = undefined;
+ var maybePromise = tryConvertToPromise(accum, this._promise);
+ var rejected = false;
+ var isPromise = maybePromise instanceof Promise;
+ if (isPromise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ maybePromise._proxyPromiseArray(this, -1);
+ } else if (maybePromise._isFulfilled()) {
+ accum = maybePromise._value();
+ this._gotAccum = true;
+ } else {
+ this._reject(maybePromise._reason());
+ rejected = true;
+ }
+ }
+ if (!(isPromise || this._zerothIsAccum)) this._gotAccum = true;
+ this._callback = fn;
+ this._accum = accum;
+ if (!rejected) async.invoke(init, this, undefined);
+}
+function init() {
+ this._init$(undefined, -5);
+}
+util.inherits(ReductionPromiseArray, PromiseArray);
+
+ReductionPromiseArray.prototype._init = function () {};
+
+ReductionPromiseArray.prototype._resolveEmptyArray = function () {
+ if (this._gotAccum || this._zerothIsAccum) {
+ this._resolve(this._preservedValues !== null
+ ? [] : this._accum);
+ }
+};
+
+ReductionPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ var values = this._values;
+ values[index] = value;
+ var length = this.length();
+ var preservedValues = this._preservedValues;
+ var isEach = preservedValues !== null;
+ var gotAccum = this._gotAccum;
+ var valuesPhase = this._valuesPhase;
+ var valuesPhaseIndex;
+ if (!valuesPhase) {
+ valuesPhase = this._valuesPhase = new Array(length);
+ for (valuesPhaseIndex=0; valuesPhaseIndex<length; ++valuesPhaseIndex) {
+ valuesPhase[valuesPhaseIndex] = 0;
+ }
+ }
+ valuesPhaseIndex = valuesPhase[index];
+
+ if (index === 0 && this._zerothIsAccum) {
+ this._accum = value;
+ this._gotAccum = gotAccum = true;
+ valuesPhase[index] = ((valuesPhaseIndex === 0)
+ ? 1 : 2);
+ } else if (index === -1) {
+ this._accum = value;
+ this._gotAccum = gotAccum = true;
+ } else {
+ if (valuesPhaseIndex === 0) {
+ valuesPhase[index] = 1;
+ } else {
+ valuesPhase[index] = 2;
+ this._accum = value;
+ }
+ }
+ if (!gotAccum) return;
+
+ var callback = this._callback;
+ var receiver = this._promise._boundTo;
+ var ret;
+
+ for (var i = this._reducingIndex; i < length; ++i) {
+ valuesPhaseIndex = valuesPhase[i];
+ if (valuesPhaseIndex === 2) {
+ this._reducingIndex = i + 1;
+ continue;
+ }
+ if (valuesPhaseIndex !== 1) return;
+ value = values[i];
+ this._promise._pushContext();
+ if (isEach) {
+ preservedValues.push(value);
+ ret = tryCatch(callback).call(receiver, value, i, length);
+ }
+ else {
+ ret = tryCatch(callback)
+ .call(receiver, this._accum, value, i, length);
+ }
+ this._promise._popContext();
+
+ if (ret === errorObj) return this._reject(ret.e);
+
+ var maybePromise = tryConvertToPromise(ret, this._promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ if (maybePromise._isPending()) {
+ valuesPhase[i] = 4;
+ return maybePromise._proxyPromiseArray(this, i);
+ } else if (maybePromise._isFulfilled()) {
+ ret = maybePromise._value();
+ } else {
+ return this._reject(maybePromise._reason());
+ }
+ }
+
+ this._reducingIndex = i + 1;
+ this._accum = ret;
+ }
+
+ this._resolve(isEach ? preservedValues : this._accum);
+};
+
+function reduce(promises, fn, initialValue, _each) {
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
+ return array.promise();
+}
+
+Promise.prototype.reduce = function (fn, initialValue) {
+ return reduce(this, fn, initialValue, null);
+};
+
+Promise.reduce = function (promises, fn, initialValue, _each) {
+ return reduce(promises, fn, initialValue, _each);
+};
+};
--- /dev/null
+"use strict";
+var schedule;
+var noAsyncScheduler = function() {
+ throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/m3OTXk\u000a");
+};
+if (require("./util.js").isNode) {
+ var version = process.versions.node.split(".").map(Number);
+ schedule = (version[0] === 0 && version[1] > 10) || (version[0] > 0)
+ ? global.setImmediate : process.nextTick;
+
+ if (!schedule) {
+ if (typeof setImmediate !== "undefined") {
+ schedule = setImmediate;
+ } else if (typeof setTimeout !== "undefined") {
+ schedule = setTimeout;
+ } else {
+ schedule = noAsyncScheduler;
+ }
+ }
+} else if (typeof MutationObserver !== "undefined") {
+ schedule = function(fn) {
+ var div = document.createElement("div");
+ var observer = new MutationObserver(fn);
+ observer.observe(div, {attributes: true});
+ return function() { div.classList.toggle("foo"); };
+ };
+ schedule.isStatic = true;
+} else if (typeof setImmediate !== "undefined") {
+ schedule = function (fn) {
+ setImmediate(fn);
+ };
+} else if (typeof setTimeout !== "undefined") {
+ schedule = function (fn) {
+ setTimeout(fn, 0);
+ };
+} else {
+ schedule = noAsyncScheduler;
+}
+module.exports = schedule;
--- /dev/null
+"use strict";
+module.exports =
+ function(Promise, PromiseArray) {
+var PromiseInspection = Promise.PromiseInspection;
+var util = require("./util.js");
+
+function SettledPromiseArray(values) {
+ this.constructor$(values);
+}
+util.inherits(SettledPromiseArray, PromiseArray);
+
+SettledPromiseArray.prototype._promiseResolved = function (index, inspection) {
+ this._values[index] = inspection;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ this._resolve(this._values);
+ }
+};
+
+SettledPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ var ret = new PromiseInspection();
+ ret._bitField = 268435456;
+ ret._settledValue = value;
+ this._promiseResolved(index, ret);
+};
+SettledPromiseArray.prototype._promiseRejected = function (reason, index) {
+ var ret = new PromiseInspection();
+ ret._bitField = 134217728;
+ ret._settledValue = reason;
+ this._promiseResolved(index, ret);
+};
+
+Promise.settle = function (promises) {
+ return new SettledPromiseArray(promises).promise();
+};
+
+Promise.prototype.settle = function () {
+ return new SettledPromiseArray(this).promise();
+};
+};
--- /dev/null
+"use strict";
+module.exports =
+function(Promise, PromiseArray, apiRejection) {
+var util = require("./util.js");
+var RangeError = require("./errors.js").RangeError;
+var AggregateError = require("./errors.js").AggregateError;
+var isArray = util.isArray;
+
+
+function SomePromiseArray(values) {
+ this.constructor$(values);
+ this._howMany = 0;
+ this._unwrap = false;
+ this._initialized = false;
+}
+util.inherits(SomePromiseArray, PromiseArray);
+
+SomePromiseArray.prototype._init = function () {
+ if (!this._initialized) {
+ return;
+ }
+ if (this._howMany === 0) {
+ this._resolve([]);
+ return;
+ }
+ this._init$(undefined, -5);
+ var isArrayResolved = isArray(this._values);
+ if (!this._isResolved() &&
+ isArrayResolved &&
+ this._howMany > this._canPossiblyFulfill()) {
+ this._reject(this._getRangeError(this.length()));
+ }
+};
+
+SomePromiseArray.prototype.init = function () {
+ this._initialized = true;
+ this._init();
+};
+
+SomePromiseArray.prototype.setUnwrap = function () {
+ this._unwrap = true;
+};
+
+SomePromiseArray.prototype.howMany = function () {
+ return this._howMany;
+};
+
+SomePromiseArray.prototype.setHowMany = function (count) {
+ this._howMany = count;
+};
+
+SomePromiseArray.prototype._promiseFulfilled = function (value) {
+ this._addFulfilled(value);
+ if (this._fulfilled() === this.howMany()) {
+ this._values.length = this.howMany();
+ if (this.howMany() === 1 && this._unwrap) {
+ this._resolve(this._values[0]);
+ } else {
+ this._resolve(this._values);
+ }
+ }
+
+};
+SomePromiseArray.prototype._promiseRejected = function (reason) {
+ this._addRejected(reason);
+ if (this.howMany() > this._canPossiblyFulfill()) {
+ var e = new AggregateError();
+ for (var i = this.length(); i < this._values.length; ++i) {
+ e.push(this._values[i]);
+ }
+ this._reject(e);
+ }
+};
+
+SomePromiseArray.prototype._fulfilled = function () {
+ return this._totalResolved;
+};
+
+SomePromiseArray.prototype._rejected = function () {
+ return this._values.length - this.length();
+};
+
+SomePromiseArray.prototype._addRejected = function (reason) {
+ this._values.push(reason);
+};
+
+SomePromiseArray.prototype._addFulfilled = function (value) {
+ this._values[this._totalResolved++] = value;
+};
+
+SomePromiseArray.prototype._canPossiblyFulfill = function () {
+ return this.length() - this._rejected();
+};
+
+SomePromiseArray.prototype._getRangeError = function (count) {
+ var message = "Input array must contain at least " +
+ this._howMany + " items but contains only " + count + " items";
+ return new RangeError(message);
+};
+
+SomePromiseArray.prototype._resolveEmptyArray = function () {
+ this._reject(this._getRangeError(0));
+};
+
+function some(promises, howMany) {
+ if ((howMany | 0) !== howMany || howMany < 0) {
+ return apiRejection("expecting a positive integer\u000a\u000a See http://goo.gl/1wAmHx\u000a");
+ }
+ var ret = new SomePromiseArray(promises);
+ var promise = ret.promise();
+ ret.setHowMany(howMany);
+ ret.init();
+ return promise;
+}
+
+Promise.some = function (promises, howMany) {
+ return some(promises, howMany);
+};
+
+Promise.prototype.some = function (howMany) {
+ return some(this, howMany);
+};
+
+Promise._SomePromiseArray = SomePromiseArray;
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise) {
+function PromiseInspection(promise) {
+ if (promise !== undefined) {
+ promise = promise._target();
+ this._bitField = promise._bitField;
+ this._settledValue = promise._settledValue;
+ }
+ else {
+ this._bitField = 0;
+ this._settledValue = undefined;
+ }
+}
+
+PromiseInspection.prototype.value = function () {
+ if (!this.isFulfilled()) {
+ throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\u000a\u000a See http://goo.gl/hc1DLj\u000a");
+ }
+ return this._settledValue;
+};
+
+PromiseInspection.prototype.error =
+PromiseInspection.prototype.reason = function () {
+ if (!this.isRejected()) {
+ throw new TypeError("cannot get rejection reason of a non-rejected promise\u000a\u000a See http://goo.gl/hPuiwB\u000a");
+ }
+ return this._settledValue;
+};
+
+PromiseInspection.prototype.isFulfilled =
+Promise.prototype._isFulfilled = function () {
+ return (this._bitField & 268435456) > 0;
+};
+
+PromiseInspection.prototype.isRejected =
+Promise.prototype._isRejected = function () {
+ return (this._bitField & 134217728) > 0;
+};
+
+PromiseInspection.prototype.isPending =
+Promise.prototype._isPending = function () {
+ return (this._bitField & 402653184) === 0;
+};
+
+PromiseInspection.prototype.isResolved =
+Promise.prototype._isResolved = function () {
+ return (this._bitField & 402653184) > 0;
+};
+
+Promise.prototype.isPending = function() {
+ return this._target()._isPending();
+};
+
+Promise.prototype.isRejected = function() {
+ return this._target()._isRejected();
+};
+
+Promise.prototype.isFulfilled = function() {
+ return this._target()._isFulfilled();
+};
+
+Promise.prototype.isResolved = function() {
+ return this._target()._isResolved();
+};
+
+Promise.prototype._value = function() {
+ return this._settledValue;
+};
+
+Promise.prototype._reason = function() {
+ this._unsetRejectionIsUnhandled();
+ return this._settledValue;
+};
+
+Promise.prototype.value = function() {
+ var target = this._target();
+ if (!target.isFulfilled()) {
+ throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\u000a\u000a See http://goo.gl/hc1DLj\u000a");
+ }
+ return target._settledValue;
+};
+
+Promise.prototype.reason = function() {
+ var target = this._target();
+ if (!target.isRejected()) {
+ throw new TypeError("cannot get rejection reason of a non-rejected promise\u000a\u000a See http://goo.gl/hPuiwB\u000a");
+ }
+ target._unsetRejectionIsUnhandled();
+ return target._settledValue;
+};
+
+
+Promise.PromiseInspection = PromiseInspection;
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var util = require("./util.js");
+var errorObj = util.errorObj;
+var isObject = util.isObject;
+
+function tryConvertToPromise(obj, context) {
+ if (isObject(obj)) {
+ if (obj instanceof Promise) {
+ return obj;
+ }
+ else if (isAnyBluebirdPromise(obj)) {
+ var ret = new Promise(INTERNAL);
+ obj._then(
+ ret._fulfillUnchecked,
+ ret._rejectUncheckedCheckError,
+ ret._progressUnchecked,
+ ret,
+ null
+ );
+ return ret;
+ }
+ var then = util.tryCatch(getThen)(obj);
+ if (then === errorObj) {
+ if (context) context._pushContext();
+ var ret = Promise.reject(then.e);
+ if (context) context._popContext();
+ return ret;
+ } else if (typeof then === "function") {
+ return doThenable(obj, then, context);
+ }
+ }
+ return obj;
+}
+
+function getThen(obj) {
+ return obj.then;
+}
+
+var hasProp = {}.hasOwnProperty;
+function isAnyBluebirdPromise(obj) {
+ return hasProp.call(obj, "_promise0");
+}
+
+function doThenable(x, then, context) {
+ var promise = new Promise(INTERNAL);
+ var ret = promise;
+ if (context) context._pushContext();
+ promise._captureStackTrace();
+ if (context) context._popContext();
+ var synchronous = true;
+ var result = util.tryCatch(then).call(x,
+ resolveFromThenable,
+ rejectFromThenable,
+ progressFromThenable);
+ synchronous = false;
+ if (promise && result === errorObj) {
+ promise._rejectCallback(result.e, true, true);
+ promise = null;
+ }
+
+ function resolveFromThenable(value) {
+ if (!promise) return;
+ if (x === value) {
+ promise._rejectCallback(
+ Promise._makeSelfResolutionError(), false, true);
+ } else {
+ promise._resolveCallback(value);
+ }
+ promise = null;
+ }
+
+ function rejectFromThenable(reason) {
+ if (!promise) return;
+ promise._rejectCallback(reason, synchronous, true);
+ promise = null;
+ }
+
+ function progressFromThenable(value) {
+ if (!promise) return;
+ if (typeof promise._progress === "function") {
+ promise._progress(value);
+ }
+ }
+ return ret;
+}
+
+return tryConvertToPromise;
+};
--- /dev/null
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var util = require("./util.js");
+var TimeoutError = Promise.TimeoutError;
+
+var afterTimeout = function (promise, message) {
+ if (!promise.isPending()) return;
+ if (typeof message !== "string") {
+ message = "operation timed out";
+ }
+ var err = new TimeoutError(message);
+ util.markAsOriginatingFromRejection(err);
+ promise._attachExtraTrace(err);
+ promise._cancel(err);
+};
+
+var afterValue = function(value) { return delay(+this).thenReturn(value); };
+var delay = Promise.delay = function (value, ms) {
+ if (ms === undefined) {
+ ms = value;
+ value = undefined;
+ var ret = new Promise(INTERNAL);
+ setTimeout(function() { ret._fulfill(); }, ms);
+ return ret;
+ }
+ ms = +ms;
+ return Promise.resolve(value)._then(afterValue, null, null, ms, undefined);
+};
+
+Promise.prototype.delay = function (ms) {
+ return delay(this, ms);
+};
+
+function successClear(value) {
+ var handle = this;
+ if (handle instanceof Number) handle = +handle;
+ clearTimeout(handle);
+ return value;
+}
+
+function failureClear(reason) {
+ var handle = this;
+ if (handle instanceof Number) handle = +handle;
+ clearTimeout(handle);
+ throw reason;
+}
+
+Promise.prototype.timeout = function (ms, message) {
+ ms = +ms;
+ var ret = this.then().cancellable();
+ ret._cancellationParent = this;
+ var handle = setTimeout(function timeoutTimeout() {
+ afterTimeout(ret, message);
+ }, ms);
+ return ret._then(successClear, failureClear, undefined, handle, undefined);
+};
+
+};
--- /dev/null
+"use strict";
+module.exports = function (Promise, apiRejection, tryConvertToPromise,
+ createContext) {
+ var TypeError = require("./errors.js").TypeError;
+ var inherits = require("./util.js").inherits;
+ var PromiseInspection = Promise.PromiseInspection;
+
+ function inspectionMapper(inspections) {
+ var len = inspections.length;
+ for (var i = 0; i < len; ++i) {
+ var inspection = inspections[i];
+ if (inspection.isRejected()) {
+ return Promise.reject(inspection.error());
+ }
+ inspections[i] = inspection._settledValue;
+ }
+ return inspections;
+ }
+
+ function thrower(e) {
+ setTimeout(function(){throw e;}, 0);
+ }
+
+ function castPreservingDisposable(thenable) {
+ var maybePromise = tryConvertToPromise(thenable);
+ if (maybePromise !== thenable &&
+ typeof thenable._isDisposable === "function" &&
+ typeof thenable._getDisposer === "function" &&
+ thenable._isDisposable()) {
+ maybePromise._setDisposable(thenable._getDisposer());
+ }
+ return maybePromise;
+ }
+ function dispose(resources, inspection) {
+ var i = 0;
+ var len = resources.length;
+ var ret = Promise.defer();
+ function iterator() {
+ if (i >= len) return ret.resolve();
+ var maybePromise = castPreservingDisposable(resources[i++]);
+ if (maybePromise instanceof Promise &&
+ maybePromise._isDisposable()) {
+ try {
+ maybePromise = tryConvertToPromise(
+ maybePromise._getDisposer().tryDispose(inspection),
+ resources.promise);
+ } catch (e) {
+ return thrower(e);
+ }
+ if (maybePromise instanceof Promise) {
+ return maybePromise._then(iterator, thrower,
+ null, null, null);
+ }
+ }
+ iterator();
+ }
+ iterator();
+ return ret.promise;
+ }
+
+ function disposerSuccess(value) {
+ var inspection = new PromiseInspection();
+ inspection._settledValue = value;
+ inspection._bitField = 268435456;
+ return dispose(this, inspection).thenReturn(value);
+ }
+
+ function disposerFail(reason) {
+ var inspection = new PromiseInspection();
+ inspection._settledValue = reason;
+ inspection._bitField = 134217728;
+ return dispose(this, inspection).thenThrow(reason);
+ }
+
+ function Disposer(data, promise, context) {
+ this._data = data;
+ this._promise = promise;
+ this._context = context;
+ }
+
+ Disposer.prototype.data = function () {
+ return this._data;
+ };
+
+ Disposer.prototype.promise = function () {
+ return this._promise;
+ };
+
+ Disposer.prototype.resource = function () {
+ if (this.promise().isFulfilled()) {
+ return this.promise().value();
+ }
+ return null;
+ };
+
+ Disposer.prototype.tryDispose = function(inspection) {
+ var resource = this.resource();
+ var context = this._context;
+ if (context !== undefined) context._pushContext();
+ var ret = resource !== null
+ ? this.doDispose(resource, inspection) : null;
+ if (context !== undefined) context._popContext();
+ this._promise._unsetDisposable();
+ this._data = null;
+ return ret;
+ };
+
+ Disposer.isDisposer = function (d) {
+ return (d != null &&
+ typeof d.resource === "function" &&
+ typeof d.tryDispose === "function");
+ };
+
+ function FunctionDisposer(fn, promise, context) {
+ this.constructor$(fn, promise, context);
+ }
+ inherits(FunctionDisposer, Disposer);
+
+ FunctionDisposer.prototype.doDispose = function (resource, inspection) {
+ var fn = this.data();
+ return fn.call(resource, resource, inspection);
+ };
+
+ function maybeUnwrapDisposer(value) {
+ if (Disposer.isDisposer(value)) {
+ this.resources[this.index]._setDisposable(value);
+ return value.promise();
+ }
+ return value;
+ }
+
+ Promise.using = function () {
+ var len = arguments.length;
+ if (len < 2) return apiRejection(
+ "you must pass at least 2 arguments to Promise.using");
+ var fn = arguments[len - 1];
+ if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
+ len--;
+ var resources = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ var resource = arguments[i];
+ if (Disposer.isDisposer(resource)) {
+ var disposer = resource;
+ resource = resource.promise();
+ resource._setDisposable(disposer);
+ } else {
+ var maybePromise = tryConvertToPromise(resource);
+ if (maybePromise instanceof Promise) {
+ resource =
+ maybePromise._then(maybeUnwrapDisposer, null, null, {
+ resources: resources,
+ index: i
+ }, undefined);
+ }
+ }
+ resources[i] = resource;
+ }
+
+ var promise = Promise.settle(resources)
+ .then(inspectionMapper)
+ .then(function(vals) {
+ promise._pushContext();
+ var ret;
+ try {
+ ret = fn.apply(undefined, vals);
+ } finally {
+ promise._popContext();
+ }
+ return ret;
+ })
+ ._then(
+ disposerSuccess, disposerFail, undefined, resources, undefined);
+ resources.promise = promise;
+ return promise;
+ };
+
+ Promise.prototype._setDisposable = function (disposer) {
+ this._bitField = this._bitField | 262144;
+ this._disposer = disposer;
+ };
+
+ Promise.prototype._isDisposable = function () {
+ return (this._bitField & 262144) > 0;
+ };
+
+ Promise.prototype._getDisposer = function () {
+ return this._disposer;
+ };
+
+ Promise.prototype._unsetDisposable = function () {
+ this._bitField = this._bitField & (~262144);
+ this._disposer = undefined;
+ };
+
+ Promise.prototype.disposer = function (fn) {
+ if (typeof fn === "function") {
+ return new FunctionDisposer(fn, this, createContext());
+ }
+ throw new TypeError();
+ };
+
+};
--- /dev/null
+"use strict";
+var es5 = require("./es5.js");
+var canEvaluate = typeof navigator == "undefined";
+var haveGetters = (function(){
+ try {
+ var o = {};
+ es5.defineProperty(o, "f", {
+ get: function () {
+ return 3;
+ }
+ });
+ return o.f === 3;
+ }
+ catch (e) {
+ return false;
+ }
+
+})();
+
+var errorObj = {e: {}};
+var tryCatchTarget;
+function tryCatcher() {
+ try {
+ return tryCatchTarget.apply(this, arguments);
+ } catch (e) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+function tryCatch(fn) {
+ tryCatchTarget = fn;
+ return tryCatcher;
+}
+
+var inherits = function(Child, Parent) {
+ var hasProp = {}.hasOwnProperty;
+
+ function T() {
+ this.constructor = Child;
+ this.constructor$ = Parent;
+ for (var propertyName in Parent.prototype) {
+ if (hasProp.call(Parent.prototype, propertyName) &&
+ propertyName.charAt(propertyName.length-1) !== "$"
+ ) {
+ this[propertyName + "$"] = Parent.prototype[propertyName];
+ }
+ }
+ }
+ T.prototype = Parent.prototype;
+ Child.prototype = new T();
+ return Child.prototype;
+};
+
+
+function isPrimitive(val) {
+ return val == null || val === true || val === false ||
+ typeof val === "string" || typeof val === "number";
+
+}
+
+function isObject(value) {
+ return !isPrimitive(value);
+}
+
+function maybeWrapAsError(maybeError) {
+ if (!isPrimitive(maybeError)) return maybeError;
+
+ return new Error(safeToString(maybeError));
+}
+
+function withAppended(target, appendee) {
+ var len = target.length;
+ var ret = new Array(len + 1);
+ var i;
+ for (i = 0; i < len; ++i) {
+ ret[i] = target[i];
+ }
+ ret[i] = appendee;
+ return ret;
+}
+
+function getDataPropertyOrDefault(obj, key, defaultValue) {
+ if (es5.isES5) {
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+ if (desc != null) {
+ return desc.get == null && desc.set == null
+ ? desc.value
+ : defaultValue;
+ }
+ } else {
+ return {}.hasOwnProperty.call(obj, key) ? obj[key] : undefined;
+ }
+}
+
+function notEnumerableProp(obj, name, value) {
+ if (isPrimitive(obj)) return obj;
+ var descriptor = {
+ value: value,
+ configurable: true,
+ enumerable: false,
+ writable: true
+ };
+ es5.defineProperty(obj, name, descriptor);
+ return obj;
+}
+
+
+var wrapsPrimitiveReceiver = (function() {
+ return this !== "string";
+}).call("string");
+
+function thrower(r) {
+ throw r;
+}
+
+var inheritedDataKeys = (function() {
+ if (es5.isES5) {
+ var oProto = Object.prototype;
+ var getKeys = Object.getOwnPropertyNames;
+ return function(obj) {
+ var ret = [];
+ var visitedKeys = Object.create(null);
+ while (obj != null && obj !== oProto) {
+ var keys;
+ try {
+ keys = getKeys(obj);
+ } catch (e) {
+ return ret;
+ }
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (visitedKeys[key]) continue;
+ visitedKeys[key] = true;
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+ if (desc != null && desc.get == null && desc.set == null) {
+ ret.push(key);
+ }
+ }
+ obj = es5.getPrototypeOf(obj);
+ }
+ return ret;
+ };
+ } else {
+ return function(obj) {
+ var ret = [];
+ /*jshint forin:false */
+ for (var key in obj) {
+ ret.push(key);
+ }
+ return ret;
+ };
+ }
+
+})();
+
+function isClass(fn) {
+ try {
+ if (typeof fn === "function") {
+ var keys = es5.names(fn.prototype);
+ if (es5.isES5) return keys.length > 1;
+ return keys.length > 0 &&
+ !(keys.length === 1 && keys[0] === "constructor");
+ }
+ return false;
+ } catch (e) {
+ return false;
+ }
+}
+
+function toFastProperties(obj) {
+ /*jshint -W027,-W055,-W031*/
+ function f() {}
+ f.prototype = obj;
+ var l = 8;
+ while (l--) new f();
+ return obj;
+ eval(obj);
+}
+
+var rident = /^[a-z$_][a-z$_0-9]*$/i;
+function isIdentifier(str) {
+ return rident.test(str);
+}
+
+function filledRange(count, prefix, suffix) {
+ var ret = new Array(count);
+ for(var i = 0; i < count; ++i) {
+ ret[i] = prefix + i + suffix;
+ }
+ return ret;
+}
+
+function safeToString(obj) {
+ try {
+ return obj + "";
+ } catch (e) {
+ return "[no string representation]";
+ }
+}
+
+function markAsOriginatingFromRejection(e) {
+ try {
+ notEnumerableProp(e, "isOperational", true);
+ }
+ catch(ignore) {}
+}
+
+function originatesFromRejection(e) {
+ if (e == null) return false;
+ return ((e instanceof Error["__BluebirdErrorTypes__"].OperationalError) ||
+ e["isOperational"] === true);
+}
+
+function canAttachTrace(obj) {
+ return obj instanceof Error && es5.propertyIsWritable(obj, "stack");
+}
+
+var ensureErrorObject = (function() {
+ if (!("stack" in new Error())) {
+ return function(value) {
+ if (canAttachTrace(value)) return value;
+ try {throw new Error(safeToString(value));}
+ catch(err) {return err;}
+ };
+ } else {
+ return function(value) {
+ if (canAttachTrace(value)) return value;
+ return new Error(safeToString(value));
+ };
+ }
+})();
+
+function classString(obj) {
+ return {}.toString.call(obj);
+}
+
+function copyDescriptors(from, to, filter) {
+ var keys = es5.names(from);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (filter(key)) {
+ es5.defineProperty(to, key, es5.getDescriptor(from, key));
+ }
+ }
+}
+
+var ret = {
+ isClass: isClass,
+ isIdentifier: isIdentifier,
+ inheritedDataKeys: inheritedDataKeys,
+ getDataPropertyOrDefault: getDataPropertyOrDefault,
+ thrower: thrower,
+ isArray: es5.isArray,
+ haveGetters: haveGetters,
+ notEnumerableProp: notEnumerableProp,
+ isPrimitive: isPrimitive,
+ isObject: isObject,
+ canEvaluate: canEvaluate,
+ errorObj: errorObj,
+ tryCatch: tryCatch,
+ inherits: inherits,
+ withAppended: withAppended,
+ maybeWrapAsError: maybeWrapAsError,
+ wrapsPrimitiveReceiver: wrapsPrimitiveReceiver,
+ toFastProperties: toFastProperties,
+ filledRange: filledRange,
+ toString: safeToString,
+ canAttachTrace: canAttachTrace,
+ ensureErrorObject: ensureErrorObject,
+ originatesFromRejection: originatesFromRejection,
+ markAsOriginatingFromRejection: markAsOriginatingFromRejection,
+ classString: classString,
+ copyDescriptors: copyDescriptors,
+ hasDevTools: typeof chrome !== "undefined" && chrome &&
+ typeof chrome.loadTimes === "function",
+ isNode: typeof process !== "undefined" &&
+ classString(process).toLowerCase() === "[object process]"
+};
+try {throw new Error(); } catch (e) {ret.lastLineError = e;}
+module.exports = ret;
--- /dev/null
+{
+ "name": "bluebird",
+ "description": "Full featured Promises/A+ implementation with exceptionally good performance",
+ "version": "2.9.25",
+ "keywords": [
+ "promise",
+ "performance",
+ "promises",
+ "promises-a",
+ "promises-aplus",
+ "async",
+ "await",
+ "deferred",
+ "deferreds",
+ "future",
+ "flow control",
+ "dsl",
+ "fluent interface"
+ ],
+ "scripts": {
+ "lint": "node scripts/jshint.js",
+ "test": "node tools/test.js",
+ "istanbul": "istanbul",
+ "prepublish": "node tools/build.js --no-debug --main --zalgo --browser --minify"
+ },
+ "homepage": "https://github.com/petkaantonov/bluebird",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/petkaantonov/bluebird.git"
+ },
+ "bugs": {
+ "url": "http://github.com/petkaantonov/bluebird/issues"
+ },
+ "license": "MIT",
+ "author": {
+ "name": "Petka Antonov",
+ "email": "petka_antonov@hotmail.com",
+ "url": "http://github.com/petkaantonov/"
+ },
+ "devDependencies": {
+ "acorn": "~0.6.0",
+ "baconjs": "^0.7.43",
+ "bluebird": "^2.9.2",
+ "body-parser": "^1.10.2",
+ "browserify": "^8.1.1",
+ "cli-table": "~0.3.1",
+ "co": "^4.2.0",
+ "cross-spawn": "^0.2.3",
+ "glob": "^4.3.2",
+ "grunt-saucelabs": "~8.4.1",
+ "highland": "^2.3.0",
+ "istanbul": "^0.3.5",
+ "jshint": "^2.6.0",
+ "jshint-stylish": "~0.2.0",
+ "mkdirp": "~0.5.0",
+ "mocha": "~2.1",
+ "open": "~0.0.5",
+ "optimist": "~0.6.1",
+ "rimraf": "~2.2.6",
+ "rx": "^2.3.25",
+ "serve-static": "^1.7.1",
+ "sinon": "~1.7.3",
+ "uglify-js": "~2.4.16"
+ },
+ "main": "./js/main/bluebird.js",
+ "browser": "./js/browser/bluebird.js",
+ "files": [
+ "js/browser",
+ "js/main",
+ "js/zalgo",
+ "LICENSE",
+ "zalgo.js"
+ ],
+ "gitHead": "13d7fbfc429422ea2dc9086371b4fd188b42038a",
+ "_id": "bluebird@2.9.25",
+ "_shasum": "6e36bd04064d9534c07160b9f7f26c5a738fe16a",
+ "_from": "bluebird@>=2.9.25 <3.0.0",
+ "_npmVersion": "2.7.1",
+ "_nodeVersion": "1.6.2",
+ "_npmUser": {
+ "name": "esailija",
+ "email": "petka_antonov@hotmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "esailija",
+ "email": "petka_antonov@hotmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "6e36bd04064d9534c07160b9f7f26c5a738fe16a",
+ "tarball": "http://registry.npmjs.org/bluebird/-/bluebird-2.9.25.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.25.tgz"
+}
--- /dev/null
+'use strict';
+var escapeStringRegexp = require('escape-string-regexp');
+var ansiStyles = require('ansi-styles');
+var stripAnsi = require('strip-ansi');
+var hasAnsi = require('has-ansi');
+var supportsColor = require('supports-color');
+var defineProps = Object.defineProperties;
+
+function Chalk(options) {
+ // detect mode if not set manually
+ this.enabled = !options || options.enabled === undefined ? supportsColor : options.enabled;
+}
+
+// use bright blue on Windows as the normal blue color is illegible
+if (process.platform === 'win32') {
+ ansiStyles.blue.open = '\u001b[94m';
+}
+
+function build(_styles) {
+ var builder = function builder() {
+ return applyStyle.apply(builder, arguments);
+ };
+ builder._styles = _styles;
+ builder.enabled = this.enabled;
+ // __proto__ is used because we must return a function, but there is
+ // no way to create a function with a different prototype.
+ builder.__proto__ = proto;
+ return builder;
+}
+
+var styles = (function () {
+ var ret = {};
+
+ Object.keys(ansiStyles).forEach(function (key) {
+ ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
+
+ ret[key] = {
+ get: function () {
+ return build.call(this, this._styles.concat(key));
+ }
+ };
+ });
+
+ return ret;
+})();
+
+var proto = defineProps(function chalk() {}, styles);
+
+function applyStyle() {
+ // support varags, but simply cast to string in case there's only one arg
+ var args = arguments;
+ var argsLen = args.length;
+ var str = argsLen !== 0 && String(arguments[0]);
+ if (argsLen > 1) {
+ // don't slice `arguments`, it prevents v8 optimizations
+ for (var a = 1; a < argsLen; a++) {
+ str += ' ' + args[a];
+ }
+ }
+
+ if (!this.enabled || !str) {
+ return str;
+ }
+
+ /*jshint validthis: true */
+ var nestedStyles = this._styles;
+
+ var i = nestedStyles.length;
+ while (i--) {
+ var code = ansiStyles[nestedStyles[i]];
+ // Replace any instances already present with a re-opening code
+ // otherwise only the part of the string until said closing code
+ // will be colored, and the rest will simply be 'plain'.
+ str = code.open + str.replace(code.closeRe, code.open) + code.close;
+ }
+
+ return str;
+}
+
+function init() {
+ var ret = {};
+
+ Object.keys(styles).forEach(function (name) {
+ ret[name] = {
+ get: function () {
+ return build.call(this, [name]);
+ }
+ };
+ });
+
+ return ret;
+}
+
+defineProps(Chalk.prototype, init());
+
+module.exports = new Chalk();
+module.exports.styles = ansiStyles;
+module.exports.hasColor = hasAnsi;
+module.exports.stripColor = stripAnsi;
+module.exports.supportsColor = supportsColor;
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+../has-ansi/cli.js
\ No newline at end of file
--- /dev/null
+../strip-ansi/cli.js
\ No newline at end of file
--- /dev/null
+../supports-color/cli.js
\ No newline at end of file
--- /dev/null
+'use strict';
+
+var styles = module.exports = {
+ modifiers: {
+ reset: [0, 0],
+ bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
+ dim: [2, 22],
+ italic: [3, 23],
+ underline: [4, 24],
+ inverse: [7, 27],
+ hidden: [8, 28],
+ strikethrough: [9, 29]
+ },
+ colors: {
+ black: [30, 39],
+ red: [31, 39],
+ green: [32, 39],
+ yellow: [33, 39],
+ blue: [34, 39],
+ magenta: [35, 39],
+ cyan: [36, 39],
+ white: [37, 39],
+ gray: [90, 39]
+ },
+ bgColors: {
+ bgBlack: [40, 49],
+ bgRed: [41, 49],
+ bgGreen: [42, 49],
+ bgYellow: [43, 49],
+ bgBlue: [44, 49],
+ bgMagenta: [45, 49],
+ bgCyan: [46, 49],
+ bgWhite: [47, 49]
+ }
+};
+
+// fix humans
+styles.colors.grey = styles.colors.gray;
+
+Object.keys(styles).forEach(function (groupName) {
+ var group = styles[groupName];
+
+ Object.keys(group).forEach(function (styleName) {
+ var style = group[styleName];
+
+ styles[styleName] = group[styleName] = {
+ open: '\u001b[' + style[0] + 'm',
+ close: '\u001b[' + style[1] + 'm'
+ };
+ });
+
+ Object.defineProperty(styles, groupName, {
+ value: group,
+ enumerable: false
+ });
+});
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+{
+ "name": "ansi-styles",
+ "version": "2.0.1",
+ "description": "ANSI escape codes for styling strings in the terminal",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sindresorhus/ansi-styles"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "da6541334e1681cb803f891fab8abf4313cc4bc1",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-styles/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-styles",
+ "_id": "ansi-styles@2.0.1",
+ "_shasum": "b033f57f93e2d28adeb8bc11138fa13da0fd20a3",
+ "_from": "ansi-styles@>=2.0.1 <3.0.0",
+ "_npmVersion": "2.1.16",
+ "_nodeVersion": "0.10.35",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "b033f57f93e2d28adeb8bc11138fa13da0fd20a3",
+ "tarball": "http://registry.npmjs.org/ansi-styles/-/ansi-styles-2.0.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.0.1.tgz"
+}
--- /dev/null
+# ansi-styles [](https://travis-ci.org/sindresorhus/ansi-styles)
+
+> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal
+
+You probably want the higher-level [chalk](https://github.com/sindresorhus/chalk) module for styling your strings.
+
+
+
+
+## Install
+
+```sh
+$ npm install --save ansi-styles
+```
+
+
+## Usage
+
+```js
+var ansi = require('ansi-styles');
+
+console.log(ansi.green.open + 'Hello world!' + ansi.green.close);
+```
+
+
+## API
+
+Each style has an `open` and `close` property.
+
+
+## Styles
+
+### Modifiers
+
+- `reset`
+- `bold`
+- `dim`
+- `italic` *(not widely supported)*
+- `underline`
+- `inverse`
+- `hidden`
+- `strikethrough` *(not widely supported)*
+
+### Colors
+
+- `black`
+- `red`
+- `green`
+- `yellow`
+- `blue`
+- `magenta`
+- `cyan`
+- `white`
+- `gray`
+
+### Background colors
+
+- `bgBlack`
+- `bgRed`
+- `bgGreen`
+- `bgYellow`
+- `bgBlue`
+- `bgMagenta`
+- `bgCyan`
+- `bgWhite`
+
+
+## Advanced usage
+
+By default you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module.
+
+- `ansi.modifiers`
+- `ansi.colors`
+- `ansi.bgColors`
+
+
+###### Example
+
+```js
+console.log(ansi.colors.green.open);
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+'use strict';
+
+var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
+
+module.exports = function (str) {
+ if (typeof str !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ return str.replace(matchOperatorsRe, '\\$&');
+};
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+{
+ "name": "escape-string-regexp",
+ "version": "1.0.3",
+ "description": "Escape RegExp special characters",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/escape-string-regexp.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "regex",
+ "regexp",
+ "re",
+ "regular",
+ "expression",
+ "escape",
+ "string",
+ "str",
+ "special",
+ "characters"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "1e446e6b4449b5f1f8868cd31bf8fd25ee37fb4b",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/escape-string-regexp/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/escape-string-regexp",
+ "_id": "escape-string-regexp@1.0.3",
+ "_shasum": "9e2d8b25bc2555c3336723750e03f099c2735bb5",
+ "_from": "escape-string-regexp@>=1.0.2 <2.0.0",
+ "_npmVersion": "2.1.16",
+ "_nodeVersion": "0.10.35",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "9e2d8b25bc2555c3336723750e03f099c2735bb5",
+ "tarball": "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# escape-string-regexp [](https://travis-ci.org/sindresorhus/escape-string-regexp)
+
+> Escape RegExp special characters
+
+
+## Install
+
+```sh
+$ npm install --save escape-string-regexp
+```
+
+
+## Usage
+
+```js
+var escapeStringRegexp = require('escape-string-regexp');
+
+var escapedString = escapeStringRegexp('how much $ for a unicorn?');
+//=> how much \$ for a unicorn\?
+
+new RegExp(escapedString);
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+#!/usr/bin/env node
+'use strict';
+var stdin = require('get-stdin');
+var pkg = require('./package.json');
+var hasAnsi = require('./');
+var argv = process.argv.slice(2);
+var input = argv[0];
+
+function help() {
+ console.log([
+ '',
+ ' ' + pkg.description,
+ '',
+ ' Usage',
+ ' has-ansi <string>',
+ ' echo <string> | has-ansi',
+ '',
+ ' Exits with code 0 if input has ANSI escape codes and 1 if not'
+ ].join('\n'));
+}
+
+function init(data) {
+ process.exit(hasAnsi(data) ? 0 : 1);
+}
+
+if (argv.indexOf('--help') !== -1) {
+ help();
+ return;
+}
+
+if (argv.indexOf('--version') !== -1) {
+ console.log(pkg.version);
+ return;
+}
+
+if (process.stdin.isTTY) {
+ if (!input) {
+ help();
+ return;
+ }
+
+ init(input);
+} else {
+ stdin(init);
+}
--- /dev/null
+'use strict';
+var ansiRegex = require('ansi-regex');
+var re = new RegExp(ansiRegex().source); // remove the `g` flag
+module.exports = re.test.bind(re);
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+'use strict';
+module.exports = function () {
+ return /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/g;
+};
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+{
+ "name": "ansi-regex",
+ "version": "1.1.1",
+ "description": "Regular expression for matching ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/ansi-regex.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha test/test.js",
+ "view-supported": "node test/viewCodes.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "47fb974630af70998157b30fad6eb5e5bd7c7cd6",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-regex/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "_id": "ansi-regex@1.1.1",
+ "_shasum": "41c847194646375e6a1a5d10c3ca054ef9fc980d",
+ "_from": "ansi-regex@>=1.1.0 <2.0.0",
+ "_npmVersion": "2.1.16",
+ "_nodeVersion": "0.10.35",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "41c847194646375e6a1a5d10c3ca054ef9fc980d",
+ "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# ansi-regex [](https://travis-ci.org/sindresorhus/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save ansi-regex
+```
+
+
+## Usage
+
+```js
+var ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001b[4mcake\u001b[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001b[4mcake\u001b[0m'.match(ansiRegex());
+//=> ['\u001b[4m', '\u001b[0m']
+```
+
+*It's a function so you can create multiple instances. Regexes with the global flag will have the `.lastIndex` property changed for each call to methods on the instance. Therefore reusing the instance with multiple calls will not work as expected for `.test()`.*
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+'use strict';
+
+module.exports = function (cb) {
+ var stdin = process.stdin;
+ var ret = '';
+
+ if (stdin.isTTY) {
+ setImmediate(cb, '');
+ return;
+ }
+
+ stdin.setEncoding('utf8');
+
+ stdin.on('readable', function () {
+ var chunk;
+
+ while (chunk = stdin.read()) {
+ ret += chunk;
+ }
+ });
+
+ stdin.on('end', function () {
+ cb(ret);
+ });
+};
+
+module.exports.buffer = function (cb) {
+ var stdin = process.stdin;
+ var ret = [];
+ var len = 0;
+
+ if (stdin.isTTY) {
+ setImmediate(cb, new Buffer(''));
+ return;
+ }
+
+ stdin.on('readable', function () {
+ var chunk;
+
+ while (chunk = stdin.read()) {
+ ret.push(chunk);
+ len += chunk.length;
+ }
+ });
+
+ stdin.on('end', function () {
+ cb(Buffer.concat(ret, len));
+ });
+};
--- /dev/null
+{
+ "name": "get-stdin",
+ "version": "4.0.1",
+ "description": "Easier stdin",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sindresorhus/get-stdin"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "node test.js && node test-buffer.js && echo unicorns | node test-real.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "std",
+ "stdin",
+ "stdio",
+ "concat",
+ "buffer",
+ "stream",
+ "process",
+ "stream"
+ ],
+ "devDependencies": {
+ "ava": "0.0.4",
+ "buffer-equal": "0.0.1"
+ },
+ "gitHead": "65c744975229b25d6cc5c7546f49b6ad9099553f",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/get-stdin/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/get-stdin",
+ "_id": "get-stdin@4.0.1",
+ "_shasum": "b968c6b0a04384324902e8bf1a5df32579a450fe",
+ "_from": "get-stdin@>=4.0.1 <5.0.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "b968c6b0a04384324902e8bf1a5df32579a450fe",
+ "tarball": "http://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
+}
--- /dev/null
+# get-stdin [](https://travis-ci.org/sindresorhus/get-stdin)
+
+> Easier stdin
+
+
+## Install
+
+```sh
+$ npm install --save get-stdin
+```
+
+
+## Usage
+
+```js
+// example.js
+var stdin = require('get-stdin');
+
+stdin(function (data) {
+ console.log(data);
+ //=> unicorns
+});
+```
+
+```sh
+$ echo unicorns | node example.js
+unicorns
+```
+
+
+## API
+
+### stdin(callback)
+
+Get `stdin` as a string.
+
+### stdin.buffer(callback)
+
+Get `stdin` as a buffer.
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+{
+ "name": "has-ansi",
+ "version": "1.0.3",
+ "description": "Check if a string has ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sindresorhus/has-ansi"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "bin": {
+ "has-ansi": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js",
+ "cli.js"
+ ],
+ "keywords": [
+ "cli",
+ "bin",
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "string",
+ "tty",
+ "escape",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern",
+ "has"
+ ],
+ "dependencies": {
+ "ansi-regex": "^1.1.0",
+ "get-stdin": "^4.0.1"
+ },
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "416428ed16f8e9718aec54cea083173af6019917",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/has-ansi/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/has-ansi",
+ "_id": "has-ansi@1.0.3",
+ "_shasum": "c0b5b1615d9e382b0ff67169d967b425e48ca538",
+ "_from": "has-ansi@>=1.0.3 <2.0.0",
+ "_npmVersion": "2.1.16",
+ "_nodeVersion": "0.10.35",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "c0b5b1615d9e382b0ff67169d967b425e48ca538",
+ "tarball": "http://registry.npmjs.org/has-ansi/-/has-ansi-1.0.3.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-1.0.3.tgz"
+}
--- /dev/null
+# has-ansi [](https://travis-ci.org/sindresorhus/has-ansi)
+
+> Check if a string has [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save has-ansi
+```
+
+
+## Usage
+
+```js
+var hasAnsi = require('has-ansi');
+
+hasAnsi('\u001b[4mcake\u001b[0m');
+//=> true
+
+hasAnsi('cake');
+//=> false
+```
+
+
+## CLI
+
+```sh
+$ npm install --global has-ansi
+```
+
+```
+$ has-ansi --help
+
+ Usage
+ has-ansi <string>
+ echo <string> | has-ansi
+
+ Exits with code 0 if input has ANSI escape codes and 1 if not
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+#!/usr/bin/env node
+'use strict';
+var fs = require('fs');
+var pkg = require('./package.json');
+var stripAnsi = require('./');
+var argv = process.argv.slice(2);
+var input = argv[0];
+
+function help() {
+ console.log([
+ '',
+ ' ' + pkg.description,
+ '',
+ ' Usage',
+ ' strip-ansi <input-file> > <output-file>',
+ ' cat <input-file> | strip-ansi > <output-file>',
+ '',
+ ' Example',
+ ' strip-ansi unicorn.txt > unicorn-stripped.txt'
+ ].join('\n'));
+}
+
+function init(data) {
+ process.stdout.write(stripAnsi(data));
+}
+
+if (argv.indexOf('--help') !== -1) {
+ help();
+ return;
+}
+
+if (argv.indexOf('--version') !== -1) {
+ console.log(pkg.version);
+ return;
+}
+
+if (!input && process.stdin.isTTY) {
+ help();
+ return;
+}
+
+if (input) {
+ init(fs.readFileSync(input, 'utf8'));
+} else {
+ process.stdin.setEncoding('utf8');
+ process.stdin.on('data', init);
+}
--- /dev/null
+'use strict';
+var ansiRegex = require('ansi-regex')();
+
+module.exports = function (str) {
+ return typeof str === 'string' ? str.replace(ansiRegex, '') : str;
+};
--- /dev/null
+'use strict';
+module.exports = function () {
+ return /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/g;
+};
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+{
+ "name": "ansi-regex",
+ "version": "1.1.1",
+ "description": "Regular expression for matching ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/ansi-regex.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha test/test.js",
+ "view-supported": "node test/viewCodes.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "47fb974630af70998157b30fad6eb5e5bd7c7cd6",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-regex/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "_id": "ansi-regex@1.1.1",
+ "_shasum": "41c847194646375e6a1a5d10c3ca054ef9fc980d",
+ "_from": "ansi-regex@>=1.0.0 <2.0.0",
+ "_npmVersion": "2.1.16",
+ "_nodeVersion": "0.10.35",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "41c847194646375e6a1a5d10c3ca054ef9fc980d",
+ "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# ansi-regex [](https://travis-ci.org/sindresorhus/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save ansi-regex
+```
+
+
+## Usage
+
+```js
+var ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001b[4mcake\u001b[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001b[4mcake\u001b[0m'.match(ansiRegex());
+//=> ['\u001b[4m', '\u001b[0m']
+```
+
+*It's a function so you can create multiple instances. Regexes with the global flag will have the `.lastIndex` property changed for each call to methods on the instance. Therefore reusing the instance with multiple calls will not work as expected for `.test()`.*
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+{
+ "name": "strip-ansi",
+ "version": "2.0.1",
+ "description": "Strip ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/strip-ansi.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
+ },
+ "bin": {
+ "strip-ansi": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js",
+ "cli.js"
+ ],
+ "keywords": [
+ "strip",
+ "trim",
+ "remove",
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "dependencies": {
+ "ansi-regex": "^1.0.0"
+ },
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "1eff0936c01f89efa312d9d51deed137259871a1",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/strip-ansi/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/strip-ansi",
+ "_id": "strip-ansi@2.0.1",
+ "_shasum": "df62c1aa94ed2f114e1d0f21fd1d50482b79a60e",
+ "_from": "strip-ansi@>=2.0.1 <3.0.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "dist": {
+ "shasum": "df62c1aa94ed2f114e1d0f21fd1d50482b79a60e",
+ "tarball": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+# strip-ansi [](https://travis-ci.org/sindresorhus/strip-ansi)
+
+> Strip [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```sh
+$ npm install --save strip-ansi
+```
+
+
+## Usage
+
+```js
+var stripAnsi = require('strip-ansi');
+
+stripAnsi('\u001b[4mcake\u001b[0m');
+//=> 'cake'
+```
+
+
+## CLI
+
+```sh
+$ npm install --global strip-ansi
+```
+
+```sh
+$ strip-ansi --help
+
+ Usage
+ strip-ansi <input-file> > <output-file>
+ cat <input-file> | strip-ansi > <output-file>
+
+ Example
+ strip-ansi unicorn.txt > unicorn-stripped.txt
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+#!/usr/bin/env node
+'use strict';
+var pkg = require('./package.json');
+var supportsColor = require('./');
+var argv = process.argv.slice(2);
+
+function help() {
+ console.log([
+ '',
+ ' ' + pkg.description,
+ '',
+ ' Usage',
+ ' supports-color',
+ '',
+ ' Exits with code 0 if color is supported and 1 if not'
+ ].join('\n'));
+}
+
+if (argv.indexOf('--help') !== -1) {
+ help();
+ return;
+}
+
+if (argv.indexOf('--version') !== -1) {
+ console.log(pkg.version);
+ return;
+}
+
+process.exit(supportsColor ? 0 : 1);
--- /dev/null
+'use strict';
+var argv = process.argv;
+
+module.exports = (function () {
+ if ('FORCE_COLOR' in process.env) {
+ return true;
+ }
+
+ if (argv.indexOf('--no-color') !== -1 ||
+ argv.indexOf('--no-colors') !== -1 ||
+ argv.indexOf('--color=false') !== -1) {
+ return false;
+ }
+
+ if (argv.indexOf('--color') !== -1 ||
+ argv.indexOf('--colors') !== -1 ||
+ argv.indexOf('--color=true') !== -1 ||
+ argv.indexOf('--color=always') !== -1) {
+ return true;
+ }
+
+ if (process.stdout && !process.stdout.isTTY) {
+ return false;
+ }
+
+ if (process.platform === 'win32') {
+ return true;
+ }
+
+ if ('COLORTERM' in process.env) {
+ return true;
+ }
+
+ if (process.env.TERM === 'dumb') {
+ return false;
+ }
+
+ if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
+ return true;
+ }
+
+ return false;
+})();
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+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.
--- /dev/null
+{
+ "name": "supports-color",
+ "version": "1.3.1",
+ "description": "Detect whether a terminal supports color",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sindresorhus/supports-color"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "bin": {
+ "supports-color": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js",
+ "cli.js"
+ ],
+ "keywords": [
+ "cli",
+ "bin",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "ansi",
+ "styles",
+ "tty",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "support",
+ "supports",
+ "capability",
+ "detect"
+ ],
+ "devDependencies": {
+ "mocha": "*",
+ "require-uncached": "^1.0.2"
+ },
+ "gitHead": "09f1b4c336cee7269b4c8b3a8880054a23fcb35e",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/supports-color/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/supports-color",
+ "_id": "supports-color@1.3.1",
+ "_shasum": "15758df09d8ff3b4acc307539fabe27095e1042d",
+ "_from": "supports-color@>=1.3.0 <2.0.0",
+ "_npmVersion": "2.5.1",
+ "_nodeVersion": "0.12.0",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "15758df09d8ff3b4acc307539fabe27095e1042d",
+ "tarball": "http://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz"
+}
--- /dev/null
+# supports-color [](https://travis-ci.org/sindresorhus/supports-color)
+
+> Detect whether a terminal supports color
+
+
+## Install
+
+```
+$ npm install --save supports-color
+```
+
+
+## Usage
+
+```js
+var supportsColor = require('supports-color');
+
+if (supportsColor) {
+ console.log('Terminal supports color');
+}
+```
+
+It obeys the `--color` and `--no-color` CLI flags.
+
+For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`.
+
+
+## CLI
+
+```
+$ npm install --global supports-color
+```
+
+```
+$ supports-color --help
+
+ Usage
+ supports-color
+
+ Exits with code 0 if color is supported and 1 if not
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+{
+ "name": "chalk",
+ "version": "1.0.0",
+ "description": "Terminal string styling done right. Much color.",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sindresorhus/chalk"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha",
+ "bench": "matcha benchmark.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "ansi",
+ "styles",
+ "tty",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "dependencies": {
+ "ansi-styles": "^2.0.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^1.0.3",
+ "strip-ansi": "^2.0.1",
+ "supports-color": "^1.3.0"
+ },
+ "devDependencies": {
+ "matcha": "^0.6.0",
+ "mocha": "*"
+ },
+ "gitHead": "8864d3563313ed15574a38dd5c9d5966080c46ce",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/chalk/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/chalk",
+ "_id": "chalk@1.0.0",
+ "_shasum": "b3cf4ed0ff5397c99c75b8f679db2f52831f96dc",
+ "_from": "chalk@>=1.0.0 <2.0.0",
+ "_npmVersion": "2.5.1",
+ "_nodeVersion": "0.12.0",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "b3cf4ed0ff5397c99c75b8f679db2f52831f96dc",
+ "tarball": "http://registry.npmjs.org/chalk/-/chalk-1.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/chalk/-/chalk-1.0.0.tgz"
+}
--- /dev/null
+<h1 align="center">
+ <br>
+ <img width="360" src="https://cdn.rawgit.com/sindresorhus/chalk/19935d6484811c5e468817f846b7b3d417d7bf4a/logo.svg" alt="chalk">
+ <br>
+ <br>
+</h1>
+
+> Terminal string styling done right
+
+[](https://travis-ci.org/sindresorhus/chalk) [](https://www.youtube.com/watch?v=Sm368W0OsHo)
+
+[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough.
+
+**Chalk is a clean and focused alternative.**
+
+
+
+
+## Why
+
+- Highly performant
+- Doesn't extend `String.prototype`
+- Expressive API
+- Ability to nest styles
+- Clean and focused
+- Auto-detects color support
+- Actively maintained
+- [Used by ~3000 modules](https://www.npmjs.com/browse/depended/chalk)
+
+
+## Install
+
+```
+$ npm install --save chalk
+```
+
+
+## Usage
+
+Chalk comes with an easy to use composable API where you just chain and nest the styles you want.
+
+```js
+var chalk = require('chalk');
+
+// style a string
+chalk.blue('Hello world!');
+
+// combine styled and normal strings
+chalk.blue('Hello') + 'World' + chalk.red('!');
+
+// compose multiple styles using the chainable API
+chalk.blue.bgRed.bold('Hello world!');
+
+// pass in multiple arguments
+chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz');
+
+// nest styles
+chalk.red('Hello', chalk.underline.bgBlue('world') + '!');
+
+// nest styles of the same type even (color, underline, background)
+chalk.green(
+ 'I am a green line ' +
+ chalk.blue.underline.bold('with a blue substring') +
+ ' that becomes green again!'
+);
+```
+
+Easily define your own themes.
+
+```js
+var chalk = require('chalk');
+var error = chalk.bold.red;
+console.log(error('Error!'));
+```
+
+Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data).
+
+```js
+var name = 'Sindre';
+console.log(chalk.green('Hello %s'), name);
+//=> Hello Sindre
+```
+
+
+## API
+
+### chalk.`<style>[.<style>...](string, [string...])`
+
+Example: `chalk.red.bold.underline('Hello', 'world');`
+
+Chain [styles](#styles) and call the last one as a method with a string argument. Order doesn't matter, and later styles take precedent in case of a conflict. This simply means that `Chalk.red.yellow.green` is equivalent to `Chalk.green`.
+
+Multiple arguments will be separated by space.
+
+### chalk.enabled
+
+Color support is automatically detected, but you can override it by setting the `enabled` property. You should however only do this in your own code as it applies globally to all chalk consumers.
+
+If you need to change this in a reusable module create a new instance:
+
+```js
+var ctx = new chalk.constructor({enabled: false});
+```
+
+### chalk.supportsColor
+
+Detect whether the terminal [supports color](https://github.com/sindresorhus/supports-color). Used internally and handled for you, but exposed for convenience.
+
+Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`.
+
+### chalk.styles
+
+Exposes the styles as [ANSI escape codes](https://github.com/sindresorhus/ansi-styles).
+
+Generally not useful, but you might need just the `.open` or `.close` escape code if you're mixing externally styled strings with your own.
+
+```js
+var chalk = require('chalk');
+
+console.log(chalk.styles.red);
+//=> {open: '\u001b[31m', close: '\u001b[39m'}
+
+console.log(chalk.styles.red.open + 'Hello' + chalk.styles.red.close);
+```
+
+### chalk.hasColor(string)
+
+Check whether a string [has color](https://github.com/sindresorhus/has-ansi).
+
+### chalk.stripColor(string)
+
+[Strip color](https://github.com/sindresorhus/strip-ansi) from a string.
+
+Can be useful in combination with `.supportsColor` to strip color on externally styled text when it's not supported.
+
+Example:
+
+```js
+var chalk = require('chalk');
+var styledString = getText();
+
+if (!chalk.supportsColor) {
+ styledString = chalk.stripColor(styledString);
+}
+```
+
+
+## Styles
+
+### Modifiers
+
+- `reset`
+- `bold`
+- `dim`
+- `italic` *(not widely supported)*
+- `underline`
+- `inverse`
+- `hidden`
+- `strikethrough` *(not widely supported)*
+
+### Colors
+
+- `black`
+- `red`
+- `green`
+- `yellow`
+- `blue` *(on Windows the bright version is used as normal blue is illegible)*
+- `magenta`
+- `cyan`
+- `white`
+- `gray`
+
+### Background colors
+
+- `bgBlack`
+- `bgRed`
+- `bgGreen`
+- `bgYellow`
+- `bgBlue`
+- `bgMagenta`
+- `bgCyan`
+- `bgWhite`
+
+
+## 256-colors
+
+Chalk does not support support anything other than the base eight colors, which guarantees it will work on all terminals and systems. Some terminals, specifically `xterm` compliant ones, will support the full range of 8-bit colors. For this the lower level [ansi-256-colors](https://github.com/jbnicolai/ansi-256-colors) package can be used.
+
+
+## Windows
+
+If you're on Windows, do yourself a favor and use [`cmder`](http://bliker.github.io/cmder/) instead of `cmd.exe`.
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
--- /dev/null
+
+2.8.1 / 2015-04-22
+==================
+
+ * Back out `support multiline description` Close #396 #397
+
+
+
+2.8.0 / 2015-04-07
+==================
+
+ * Add `process.execArg` support, execution args like `--harmony` will be passed to sub-commands #387 @DigitalIO @zhiyelee
+ * Fix bug in Git-style sub-commands #372 @zhiyelee
+ * Allow commands to be hidden from help #383 @tonylukasavage
+ * When git-style sub-commands are in use, yet none are called, display help #382 @claylo
+ * Add ability to specify arguments syntax for top-level command #258 @rrthomas
+ * Support multiline descriptions #208 @zxqfox
+
+2.7.1 / 2015-03-11
+==================
+
+ * Revert #347 (fix collisions when option and first arg have same name) which causes a bug in #367.
+
+2.7.0 / 2015-03-09
+==================
+
+ * Fix git-style bug when installed globally. Close #335 #349 @zhiyelee
+ * Fix collisions when option and first arg have same name. Close #346 #347 @tonylukasavage
+ * Add support for camelCase on `opts()`. Close #353 @nkzawa
+ * Add node.js 0.12 and io.js to travis.yml
+ * Allow RegEx options. #337 @palanik
+ * Fixes exit code when sub-command failing. Close #260 #332 @pirelenito
+ * git-style `bin` files in $PATH make sense. Close #196 #327 @zhiyelee
+
+2.6.0 / 2014-12-30
+==================
+
+ * added `Command#allowUnknownOption` method. Close #138 #318 @doozr @zhiyelee
+ * Add application description to the help msg. Close #112 @dalssoft
+
+2.5.1 / 2014-12-15
+==================
+
+ * fixed two bugs incurred by variadic arguments. Close #291 @Quentin01 #302 @zhiyelee
+
+2.5.0 / 2014-10-24
+==================
+
+ * add support for variadic arguments. Closes #277 @whitlockjc
+
+2.4.0 / 2014-10-17
+==================
+
+ * fixed a bug on executing the coercion function of subcommands option. Closes #270
+ * added `Command.prototype.name` to retrieve command name. Closes #264 #266 @tonylukasavage
+ * added `Command.prototype.opts` to retrieve all the options as a simple object of key-value pairs. Closes #262 @tonylukasavage
+ * fixed a bug on subcommand name. Closes #248 @jonathandelgado
+ * fixed function normalize doesn’t honor option terminator. Closes #216 @abbr
+
+2.3.0 / 2014-07-16
+==================
+
+ * add command alias'. Closes PR #210
+ * fix: Typos. Closes #99
+ * fix: Unused fs module. Closes #217
+
+2.2.0 / 2014-03-29
+==================
+
+ * add passing of previous option value
+ * fix: support subcommands on windows. Closes #142
+ * Now the defaultValue passed as the second argument of the coercion function.
+
+2.1.0 / 2013-11-21
+==================
+
+ * add: allow cflag style option params, unit test, fixes #174
+
+2.0.0 / 2013-07-18
+==================
+
+ * remove input methods (.prompt, .confirm, etc)
+
+1.3.2 / 2013-07-18
+==================
+
+ * add support for sub-commands to co-exist with the original command
+
+1.3.1 / 2013-07-18
+==================
+
+ * add quick .runningCommand hack so you can opt-out of other logic when running a sub command
+
+1.3.0 / 2013-07-09
+==================
+
+ * add EACCES error handling
+ * fix sub-command --help
+
+1.2.0 / 2013-06-13
+==================
+
+ * allow "-" hyphen as an option argument
+ * support for RegExp coercion
+
+1.1.1 / 2012-11-20
+==================
+
+ * add more sub-command padding
+ * fix .usage() when args are present. Closes #106
+
+1.1.0 / 2012-11-16
+==================
+
+ * add git-style executable subcommand support. Closes #94
+
+1.0.5 / 2012-10-09
+==================
+
+ * fix `--name` clobbering. Closes #92
+ * fix examples/help. Closes #89
+
+1.0.4 / 2012-09-03
+==================
+
+ * add `outputHelp()` method.
+
+1.0.3 / 2012-08-30
+==================
+
+ * remove invalid .version() defaulting
+
+1.0.2 / 2012-08-24
+==================
+
+ * add `--foo=bar` support [arv]
+ * fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]
+
+1.0.1 / 2012-08-03
+==================
+
+ * fix issue #56
+ * fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())
+
+1.0.0 / 2012-07-05
+==================
+
+ * add support for optional option descriptions
+ * add defaulting of `.version()` to package.json's version
+
+0.6.1 / 2012-06-01
+==================
+
+ * Added: append (yes or no) on confirmation
+ * Added: allow node.js v0.7.x
+
+0.6.0 / 2012-04-10
+==================
+
+ * Added `.prompt(obj, callback)` support. Closes #49
+ * Added default support to .choose(). Closes #41
+ * Fixed the choice example
+
+0.5.1 / 2011-12-20
+==================
+
+ * Fixed `password()` for recent nodes. Closes #36
+
+0.5.0 / 2011-12-04
+==================
+
+ * Added sub-command option support [itay]
+
+0.4.3 / 2011-12-04
+==================
+
+ * Fixed custom help ordering. Closes #32
+
+0.4.2 / 2011-11-24
+==================
+
+ * Added travis support
+ * Fixed: line-buffered input automatically trimmed. Closes #31
+
+0.4.1 / 2011-11-18
+==================
+
+ * Removed listening for "close" on --help
+
+0.4.0 / 2011-11-15
+==================
+
+ * Added support for `--`. Closes #24
+
+0.3.3 / 2011-11-14
+==================
+
+ * Fixed: wait for close event when writing help info [Jerry Hamlet]
+
+0.3.2 / 2011-11-01
+==================
+
+ * Fixed long flag definitions with values [felixge]
+
+0.3.1 / 2011-10-31
+==================
+
+ * Changed `--version` short flag to `-V` from `-v`
+ * Changed `.version()` so it's configurable [felixge]
+
+0.3.0 / 2011-10-31
+==================
+
+ * Added support for long flags only. Closes #18
+
+0.2.1 / 2011-10-24
+==================
+
+ * "node": ">= 0.4.x < 0.7.0". Closes #20
+
+0.2.0 / 2011-09-26
+==================
+
+ * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]
+
+0.1.0 / 2011-08-24
+==================
+
+ * Added support for custom `--help` output
+
+0.0.5 / 2011-08-18
+==================
+
+ * Changed: when the user enters nothing prompt for password again
+ * Fixed issue with passwords beginning with numbers [NuckChorris]
+
+0.0.4 / 2011-08-15
+==================
+
+ * Fixed `Commander#args`
+
+0.0.3 / 2011-08-15
+==================
+
+ * Added default option value support
+
+0.0.2 / 2011-08-15
+==================
+
+ * Added mask support to `Command#password(str[, mask], fn)`
+ * Added `Command#password(str, fn)`
+
+0.0.1 / 2010-01-03
+==================
+
+ * Initial release
--- /dev/null
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+
+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.
--- /dev/null
+# Commander.js
+
+
+[](http://travis-ci.org/tj/commander.js)
+[](https://www.npmjs.org/package/commander)
+[](https://www.npmjs.org/package/commander)
+[](https://gitter.im/tj/commander.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+ The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/tj/commander).
+ [API documentation](http://tj.github.com/commander.js/)
+
+
+## Installation
+
+ $ npm install commander
+
+## Option parsing
+
+ Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-p, --peppers', 'Add peppers')
+ .option('-P, --pineapple', 'Add pineapple')
+ .option('-b, --bbq-sauce', 'Add bbq sauce')
+ .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
+ .parse(process.argv);
+
+console.log('you ordered a pizza with:');
+if (program.peppers) console.log(' - peppers');
+if (program.pineapple) console.log(' - pineapple');
+if (program.bbqSauce) console.log(' - bbq');
+console.log(' - %s cheese', program.cheese);
+```
+
+ Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc.
+
+
+## Coercion
+
+```js
+function range(val) {
+ return val.split('..').map(Number);
+}
+
+function list(val) {
+ return val.split(',');
+}
+
+function collect(val, memo) {
+ memo.push(val);
+ return memo;
+}
+
+function increaseVerbosity(v, total) {
+ return total + 1;
+}
+
+program
+ .version('0.0.1')
+ .usage('[options] <file ...>')
+ .option('-i, --integer <n>', 'An integer argument', parseInt)
+ .option('-f, --float <n>', 'A float argument', parseFloat)
+ .option('-r, --range <a>..<b>', 'A range', range)
+ .option('-l, --list <items>', 'A list', list)
+ .option('-o, --optional [value]', 'An optional value')
+ .option('-c, --collect [value]', 'A repeatable value', collect, [])
+ .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
+ .parse(process.argv);
+
+console.log(' int: %j', program.integer);
+console.log(' float: %j', program.float);
+console.log(' optional: %j', program.optional);
+program.range = program.range || [];
+console.log(' range: %j..%j', program.range[0], program.range[1]);
+console.log(' list: %j', program.list);
+console.log(' collect: %j', program.collect);
+console.log(' verbosity: %j', program.verbose);
+console.log(' args: %j', program.args);
+```
+
+## Regular Expression
+```js
+program
+ .version('0.0.1')
+ .option('-s --size <size>', 'Pizza size', /^(large|medium|small)$/i, 'medium')
+ .option('-d --drink [drink]', 'Drink', /^(coke|pepsi|izze)$/i)
+ .parse(process.argv);
+
+console.log(' size: %j', program.size);
+console.log(' drink: %j', program.drink);
+```
+
+## Variadic arguments
+
+ The last argument of a command can be variadic, and only the last argument. To make an argument variadic you have to
+ append `...` to the argument name. Here is an example:
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .command('rmdir <dir> [otherDirs...]')
+ .action(function (dir, otherDirs) {
+ console.log('rmdir %s', dir);
+ if (otherDirs) {
+ otherDirs.forEach(function (oDir) {
+ console.log('rmdir %s', oDir);
+ });
+ }
+ });
+
+program.parse(process.argv);
+```
+
+ An `Array` is used for the value of a variadic argument. This applies to `program.args` as well as the argument passed
+ to your action as demonstrated above.
+
+## Specify the argument syntax
+
+```js
+#!/usr/bin/env node
+
+var program = require('../');
+
+program
+ .version('0.0.1')
+ .arguments('<cmd> [env]')
+ .action(function (cmd, env) {
+ cmdValue = cmd;
+ envValue = env;
+ });
+
+program.parse(process.argv);
+
+if (typeof cmdValue === 'undefined') {
+ console.error('no command given!');
+ process.exit(1);
+}
+console.log('command:', cmdValue);
+console.log('environment:', envValue || "no environment given");
+```
+
+## Git-style sub-commands
+
+```js
+// file: ./examples/pm
+var program = require('..');
+
+program
+ .version('0.0.1')
+ .command('install [name]', 'install one or more packages')
+ .command('search [query]', 'search with optional query')
+ .command('list', 'list packages installed')
+ .parse(process.argv);
+```
+
+When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools.
+The commander will try to search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-command`, like `pm-install`, `pm-search`.
+
+If the program is designed to be installed globally, make sure the executables have proper modes, like `755`.
+
+### `--harmony`
+
+You can enable `--harmony` option in two ways:
+* Use `#! /usr/bin/env node --harmony` in the sub-commands scripts. Note some os version don’t support this pattern.
+* Use the `--harmony` option when call the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning sub-command process.
+
+## Automated --help
+
+ The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:
+
+```
+ $ ./examples/pizza --help
+
+ Usage: pizza [options]
+
+ An application for pizzas ordering
+
+ Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -p, --peppers Add peppers
+ -P, --pineapple Add pineapple
+ -b, --bbq Add bbq sauce
+ -c, --cheese <type> Add the specified type of cheese [marble]
+ -C, --no-cheese You do not want any cheese
+
+```
+
+## Custom help
+
+ You can display arbitrary `-h, --help` information
+ by listening for "--help". Commander will automatically
+ exit once you are done so that the remainder of your program
+ does not execute causing undesired behaviours, for example
+ in the following executable "stuff" will not output when
+ `--help` is used.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-f, --foo', 'enable some foo')
+ .option('-b, --bar', 'enable some bar')
+ .option('-B, --baz', 'enable some baz');
+
+// must be before .parse() since
+// node's emit() is immediate
+
+program.on('--help', function(){
+ console.log(' Examples:');
+ console.log('');
+ console.log(' $ custom-help --help');
+ console.log(' $ custom-help -h');
+ console.log('');
+});
+
+program.parse(process.argv);
+
+console.log('stuff');
+```
+
+Yields the following help output when `node script-name.js -h` or `node script-name.js --help` are run:
+
+```
+
+Usage: custom-help [options]
+
+Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -f, --foo enable some foo
+ -b, --bar enable some bar
+ -B, --baz enable some baz
+
+Examples:
+
+ $ custom-help --help
+ $ custom-help -h
+
+```
+
+## .outputHelp()
+
+Output help information without exiting.
+
+If you want to display help by default (e.g. if no command was provided), you can use something like:
+
+```js
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .command('getstream [url]', 'get stream URL')
+ .parse(process.argv);
+
+ if (!process.argv.slice(2).length) {
+ program.outputHelp();
+ }
+```
+
+## .help()
+
+ Output help information and exit immediately.
+
+## Examples
+
+```js
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-C, --chdir <path>', 'change the working directory')
+ .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+ .option('-T, --no-tests', 'ignore test hook')
+
+program
+ .command('setup [env]')
+ .description('run setup commands for all envs')
+ .option("-s, --setup_mode [mode]", "Which setup mode to use")
+ .action(function(env, options){
+ var mode = options.setup_mode || "normal";
+ env = env || 'all';
+ console.log('setup for %s env(s) with %s mode', env, mode);
+ });
+
+program
+ .command('exec <cmd>')
+ .alias('ex')
+ .description('execute the given remote cmd')
+ .option("-e, --exec_mode <mode>", "Which exec mode to use")
+ .action(function(cmd, options){
+ console.log('exec "%s" using %s mode', cmd, options.exec_mode);
+ }).on('--help', function() {
+ console.log(' Examples:');
+ console.log();
+ console.log(' $ deploy exec sequential');
+ console.log(' $ deploy exec async');
+ console.log();
+ });
+
+program
+ .command('*')
+ .action(function(env){
+ console.log('deploying "%s"', env);
+ });
+
+program.parse(process.argv);
+```
+
+More Demos can be found in the [examples](https://github.com/tj/commander.js/tree/master/examples) directory.
+
+## License
+
+MIT
+
--- /dev/null
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter;
+var spawn = require('child_process').spawn;
+var readlink = require('graceful-readlink').readlinkSync;
+var path = require('path');
+var dirname = path.dirname;
+var basename = path.basename;
+var fs = require('fs');
+
+/**
+ * Expose the root command.
+ */
+
+exports = module.exports = new Command();
+
+/**
+ * Expose `Command`.
+ */
+
+exports.Command = Command;
+
+/**
+ * Expose `Option`.
+ */
+
+exports.Option = Option;
+
+/**
+ * Initialize a new `Option` with the given `flags` and `description`.
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @api public
+ */
+
+function Option(flags, description) {
+ this.flags = flags;
+ this.required = ~flags.indexOf('<');
+ this.optional = ~flags.indexOf('[');
+ this.bool = !~flags.indexOf('-no-');
+ flags = flags.split(/[ ,|]+/);
+ if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
+ this.long = flags.shift();
+ this.description = description || '';
+}
+
+/**
+ * Return option name.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Option.prototype.name = function() {
+ return this.long
+ .replace('--', '')
+ .replace('no-', '');
+};
+
+/**
+ * Check if `arg` matches the short or long flag.
+ *
+ * @param {String} arg
+ * @return {Boolean}
+ * @api private
+ */
+
+Option.prototype.is = function(arg) {
+ return arg == this.short || arg == this.long;
+};
+
+/**
+ * Initialize a new `Command`.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+function Command(name) {
+ this.commands = [];
+ this.options = [];
+ this._execs = [];
+ this._allowUnknownOption = false;
+ this._args = [];
+ this._name = name;
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+Command.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Add command `name`.
+ *
+ * The `.action()` callback is invoked when the
+ * command `name` is specified via __ARGV__,
+ * and the remaining arguments are applied to the
+ * function for access.
+ *
+ * When the `name` is "*" an un-matched command
+ * will be passed as the first arg, followed by
+ * the rest of __ARGV__ remaining.
+ *
+ * Examples:
+ *
+ * program
+ * .version('0.0.1')
+ * .option('-C, --chdir <path>', 'change the working directory')
+ * .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+ * .option('-T, --no-tests', 'ignore test hook')
+ *
+ * program
+ * .command('setup')
+ * .description('run remote setup commands')
+ * .action(function() {
+ * console.log('setup');
+ * });
+ *
+ * program
+ * .command('exec <cmd>')
+ * .description('run the given remote command')
+ * .action(function(cmd) {
+ * console.log('exec "%s"', cmd);
+ * });
+ *
+ * program
+ * .command('teardown <dir> [otherDirs...]')
+ * .description('run teardown commands')
+ * .action(function(dir, otherDirs) {
+ * console.log('dir "%s"', dir);
+ * if (otherDirs) {
+ * otherDirs.forEach(function (oDir) {
+ * console.log('dir "%s"', oDir);
+ * });
+ * }
+ * });
+ *
+ * program
+ * .command('*')
+ * .description('deploy the given env')
+ * .action(function(env) {
+ * console.log('deploying "%s"', env);
+ * });
+ *
+ * program.parse(process.argv);
+ *
+ * @param {String} name
+ * @param {String} [desc] for git-style sub-commands
+ * @return {Command} the new command
+ * @api public
+ */
+
+Command.prototype.command = function(name, desc, opts) {
+ opts = opts || {};
+ var args = name.split(/ +/);
+ var cmd = new Command(args.shift());
+
+ if (desc) {
+ cmd.description(desc);
+ this.executables = true;
+ this._execs[cmd._name] = true;
+ }
+
+ cmd._noHelp = !!opts.noHelp;
+ this.commands.push(cmd);
+ cmd.parseExpectedArgs(args);
+ cmd.parent = this;
+
+ if (desc) return this;
+ return cmd;
+};
+
+/**
+ * Define argument syntax for the top-level command.
+ *
+ * @api public
+ */
+
+Command.prototype.arguments = function (desc) {
+ return this.parseExpectedArgs(desc.split(/ +/));
+}
+
+/**
+ * Add an implicit `help [cmd]` subcommand
+ * which invokes `--help` for the given command.
+ *
+ * @api private
+ */
+
+Command.prototype.addImplicitHelpCommand = function() {
+ this.command('help [cmd]', 'display help for [cmd]');
+};
+
+/**
+ * Parse expected `args`.
+ *
+ * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parseExpectedArgs = function(args) {
+ if (!args.length) return;
+ var self = this;
+ args.forEach(function(arg) {
+ var argDetails = {
+ required: false,
+ name: '',
+ variadic: false
+ };
+
+ switch (arg[0]) {
+ case '<':
+ argDetails.required = true;
+ argDetails.name = arg.slice(1, -1);
+ break;
+ case '[':
+ argDetails.name = arg.slice(1, -1);
+ break;
+ }
+
+ if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {
+ argDetails.variadic = true;
+ argDetails.name = argDetails.name.slice(0, -3);
+ }
+ if (argDetails.name) {
+ self._args.push(argDetails);
+ }
+ });
+ return this;
+};
+
+/**
+ * Register callback `fn` for the command.
+ *
+ * Examples:
+ *
+ * program
+ * .command('help')
+ * .description('display verbose help')
+ * .action(function() {
+ * // output help here
+ * });
+ *
+ * @param {Function} fn
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.action = function(fn) {
+ var self = this;
+ var listener = function(args, unknown) {
+ // Parse any so-far unknown options
+ args = args || [];
+ unknown = unknown || [];
+
+ var parsed = self.parseOptions(unknown);
+
+ // Output help if necessary
+ outputHelpIfNecessary(self, parsed.unknown);
+
+ // If there are still any unknown options, then we simply
+ // die, unless someone asked for help, in which case we give it
+ // to them, and then we die.
+ if (parsed.unknown.length > 0) {
+ self.unknownOption(parsed.unknown[0]);
+ }
+
+ // Leftover arguments need to be pushed back. Fixes issue #56
+ if (parsed.args.length) args = parsed.args.concat(args);
+
+ self._args.forEach(function(arg, i) {
+ if (arg.required && null == args[i]) {
+ self.missingArgument(arg.name);
+ } else if (arg.variadic) {
+ if (i !== self._args.length - 1) {
+ self.variadicArgNotLast(arg.name);
+ }
+
+ args[i] = args.splice(i);
+ }
+ });
+
+ // Always append ourselves to the end of the arguments,
+ // to make sure we match the number of arguments the user
+ // expects
+ if (self._args.length) {
+ args[self._args.length] = self;
+ } else {
+ args.push(self);
+ }
+
+ fn.apply(self, args);
+ };
+ var parent = this.parent || this;
+ var name = parent === this ? '*' : this._name;
+ parent.on(name, listener);
+ if (this._alias) parent.on(this._alias, listener);
+ return this;
+};
+
+/**
+ * Define option with `flags`, `description` and optional
+ * coercion `fn`.
+ *
+ * The `flags` string should contain both the short and long flags,
+ * separated by comma, a pipe or space. The following are all valid
+ * all will output this way when `--help` is used.
+ *
+ * "-p, --pepper"
+ * "-p|--pepper"
+ * "-p --pepper"
+ *
+ * Examples:
+ *
+ * // simple boolean defaulting to false
+ * program.option('-p, --pepper', 'add pepper');
+ *
+ * --pepper
+ * program.pepper
+ * // => Boolean
+ *
+ * // simple boolean defaulting to true
+ * program.option('-C, --no-cheese', 'remove cheese');
+ *
+ * program.cheese
+ * // => true
+ *
+ * --no-cheese
+ * program.cheese
+ * // => false
+ *
+ * // required argument
+ * program.option('-C, --chdir <path>', 'change the working directory');
+ *
+ * --chdir /tmp
+ * program.chdir
+ * // => "/tmp"
+ *
+ * // optional argument
+ * program.option('-c, --cheese [type]', 'add cheese [marble]');
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @param {Function|Mixed} fn or default
+ * @param {Mixed} defaultValue
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.option = function(flags, description, fn, defaultValue) {
+ var self = this
+ , option = new Option(flags, description)
+ , oname = option.name()
+ , name = camelcase(oname);
+
+ // default as 3rd arg
+ if (typeof fn != 'function') {
+ if (fn instanceof RegExp) {
+ var regex = fn;
+ fn = function(val, def) {
+ var m = regex.exec(val);
+ return m ? m[0] : def;
+ }
+ }
+ else {
+ defaultValue = fn;
+ fn = null;
+ }
+ }
+
+ // preassign default value only for --no-*, [optional], or <required>
+ if (false == option.bool || option.optional || option.required) {
+ // when --no-* we make sure default is true
+ if (false == option.bool) defaultValue = true;
+ // preassign only if we have a default
+ if (undefined !== defaultValue) self[name] = defaultValue;
+ }
+
+ // register the option
+ this.options.push(option);
+
+ // when it's passed assign the value
+ // and conditionally invoke the callback
+ this.on(oname, function(val) {
+ // coercion
+ if (null !== val && fn) val = fn(val, undefined === self[name]
+ ? defaultValue
+ : self[name]);
+
+ // unassigned or bool
+ if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
+ // if no value, bool true, and we have a default, then use it!
+ if (null == val) {
+ self[name] = option.bool
+ ? defaultValue || true
+ : false;
+ } else {
+ self[name] = val;
+ }
+ } else if (null !== val) {
+ // reassign
+ self[name] = val;
+ }
+ });
+
+ return this;
+};
+
+/**
+ * Allow unknown options on the command line.
+ *
+ * @param {Boolean} arg if `true` or omitted, no error will be thrown
+ * for unknown options.
+ * @api public
+ */
+Command.prototype.allowUnknownOption = function(arg) {
+ this._allowUnknownOption = arguments.length === 0 || arg;
+ return this;
+};
+
+/**
+ * Parse `argv`, settings options and invoking commands when defined.
+ *
+ * @param {Array} argv
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parse = function(argv) {
+ // implicit help
+ if (this.executables) this.addImplicitHelpCommand();
+
+ // store raw args
+ this.rawArgs = argv;
+
+ // guess name
+ this._name = this._name || basename(argv[1], '.js');
+
+ // github-style sub-commands with no sub-command
+ if (this.executables && argv.length < 3) {
+ // this user needs help
+ argv.push('--help');
+ }
+
+ // process argv
+ var parsed = this.parseOptions(this.normalize(argv.slice(2)));
+ var args = this.args = parsed.args;
+
+ var result = this.parseArgs(this.args, parsed.unknown);
+
+ // executable sub-commands
+ var name = result.args[0];
+ if (this._execs[name] && typeof this._execs[name] != "function") {
+ return this.executeSubCommand(argv, args, parsed.unknown);
+ }
+
+ return result;
+};
+
+/**
+ * Execute a sub-command executable.
+ *
+ * @param {Array} argv
+ * @param {Array} args
+ * @param {Array} unknown
+ * @api private
+ */
+
+Command.prototype.executeSubCommand = function(argv, args, unknown) {
+ args = args.concat(unknown);
+
+ if (!args.length) this.help();
+ if ('help' == args[0] && 1 == args.length) this.help();
+
+ // <cmd> --help
+ if ('help' == args[0]) {
+ args[0] = args[1];
+ args[1] = '--help';
+ }
+
+ // executable
+ var f = argv[1];
+ // name of the subcommand, link `pm-install`
+ var bin = basename(f, '.js') + '-' + args[0];
+
+
+ // In case of globally installed, get the base dir where executable
+ // subcommand file should be located at
+ var baseDir
+ , link = readlink(f);
+
+ // when symbolink is relative path
+ if (link !== f && link.charAt(0) !== '/') {
+ link = path.join(dirname(f), link)
+ }
+ baseDir = dirname(link);
+
+ // prefer local `./<bin>` to bin in the $PATH
+ var localBin = path.join(baseDir, bin);
+
+ // whether bin file is a js script with explicit `.js` extension
+ var isExplicitJS = false;
+ if (exists(localBin + '.js')) {
+ bin = localBin + '.js';
+ isExplicitJS = true;
+ } else if (exists(localBin)) {
+ bin = localBin;
+ }
+
+ args = args.slice(1);
+
+ var proc;
+ if (process.platform !== 'win32') {
+ if (isExplicitJS) {
+ args.unshift(localBin);
+ // add executable arguments to spawn
+ args = (process.execArgv || []).concat(args);
+
+ proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
+ } else {
+ proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
+ }
+ } else {
+ args.unshift(localBin);
+ proc = spawn(process.execPath, args, { stdio: 'inherit'});
+ }
+
+ proc.on('close', process.exit.bind(process));
+ proc.on('error', function(err) {
+ if (err.code == "ENOENT") {
+ console.error('\n %s(1) does not exist, try --help\n', bin);
+ } else if (err.code == "EACCES") {
+ console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
+ }
+ process.exit(1);
+ });
+
+ this.runningCommand = proc;
+};
+
+/**
+ * Normalize `args`, splitting joined short flags. For example
+ * the arg "-abc" is equivalent to "-a -b -c".
+ * This also normalizes equal sign and splits "--abc=def" into "--abc def".
+ *
+ * @param {Array} args
+ * @return {Array}
+ * @api private
+ */
+
+Command.prototype.normalize = function(args) {
+ var ret = []
+ , arg
+ , lastOpt
+ , index;
+
+ for (var i = 0, len = args.length; i < len; ++i) {
+ arg = args[i];
+ if (i > 0) {
+ lastOpt = this.optionFor(args[i-1]);
+ }
+
+ if (arg === '--') {
+ // Honor option terminator
+ ret = ret.concat(args.slice(i));
+ break;
+ } else if (lastOpt && lastOpt.required) {
+ ret.push(arg);
+ } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
+ arg.slice(1).split('').forEach(function(c) {
+ ret.push('-' + c);
+ });
+ } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
+ ret.push(arg.slice(0, index), arg.slice(index + 1));
+ } else {
+ ret.push(arg);
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Parse command `args`.
+ *
+ * When listener(s) are available those
+ * callbacks are invoked, otherwise the "*"
+ * event is emitted and those actions are invoked.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api private
+ */
+
+Command.prototype.parseArgs = function(args, unknown) {
+ var name;
+
+ if (args.length) {
+ name = args[0];
+ if (this.listeners(name).length) {
+ this.emit(args.shift(), args, unknown);
+ } else {
+ this.emit('*', args);
+ }
+ } else {
+ outputHelpIfNecessary(this, unknown);
+
+ // If there were no args and we have unknown options,
+ // then they are extraneous and we need to error.
+ if (unknown.length > 0) {
+ this.unknownOption(unknown[0]);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Return an option matching `arg` if any.
+ *
+ * @param {String} arg
+ * @return {Option}
+ * @api private
+ */
+
+Command.prototype.optionFor = function(arg) {
+ for (var i = 0, len = this.options.length; i < len; ++i) {
+ if (this.options[i].is(arg)) {
+ return this.options[i];
+ }
+ }
+};
+
+/**
+ * Parse options from `argv` returning `argv`
+ * void of these options.
+ *
+ * @param {Array} argv
+ * @return {Array}
+ * @api public
+ */
+
+Command.prototype.parseOptions = function(argv) {
+ var args = []
+ , len = argv.length
+ , literal
+ , option
+ , arg;
+
+ var unknownOptions = [];
+
+ // parse options
+ for (var i = 0; i < len; ++i) {
+ arg = argv[i];
+
+ // literal args after --
+ if ('--' == arg) {
+ literal = true;
+ continue;
+ }
+
+ if (literal) {
+ args.push(arg);
+ continue;
+ }
+
+ // find matching Option
+ option = this.optionFor(arg);
+
+ // option is defined
+ if (option) {
+ // requires arg
+ if (option.required) {
+ arg = argv[++i];
+ if (null == arg) return this.optionMissingArgument(option);
+ this.emit(option.name(), arg);
+ // optional arg
+ } else if (option.optional) {
+ arg = argv[i+1];
+ if (null == arg || ('-' == arg[0] && '-' != arg)) {
+ arg = null;
+ } else {
+ ++i;
+ }
+ this.emit(option.name(), arg);
+ // bool
+ } else {
+ this.emit(option.name());
+ }
+ continue;
+ }
+
+ // looks like an option
+ if (arg.length > 1 && '-' == arg[0]) {
+ unknownOptions.push(arg);
+
+ // If the next argument looks like it might be
+ // an argument for this option, we pass it on.
+ // If it isn't, then it'll simply be ignored
+ if (argv[i+1] && '-' != argv[i+1][0]) {
+ unknownOptions.push(argv[++i]);
+ }
+ continue;
+ }
+
+ // arg
+ args.push(arg);
+ }
+
+ return { args: args, unknown: unknownOptions };
+};
+
+/**
+ * Return an object containing options as key-value pairs
+ *
+ * @return {Object}
+ * @api public
+ */
+Command.prototype.opts = function() {
+ var result = {}
+ , len = this.options.length;
+
+ for (var i = 0 ; i < len; i++) {
+ var key = camelcase(this.options[i].name());
+ result[key] = key === 'version' ? this._version : this[key];
+ }
+ return result;
+};
+
+/**
+ * Argument `name` is missing.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.missingArgument = function(name) {
+ console.error();
+ console.error(" error: missing required argument `%s'", name);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * `Option` is missing an argument, but received `flag` or nothing.
+ *
+ * @param {String} option
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.optionMissingArgument = function(option, flag) {
+ console.error();
+ if (flag) {
+ console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
+ } else {
+ console.error(" error: option `%s' argument missing", option.flags);
+ }
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Unknown option `flag`.
+ *
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.unknownOption = function(flag) {
+ if (this._allowUnknownOption) return;
+ console.error();
+ console.error(" error: unknown option `%s'", flag);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Variadic argument with `name` is not the last argument as required.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.variadicArgNotLast = function(name) {
+ console.error();
+ console.error(" error: variadic arguments must be last `%s'", name);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Set the program version to `str`.
+ *
+ * This method auto-registers the "-V, --version" flag
+ * which will print the version number when passed.
+ *
+ * @param {String} str
+ * @param {String} flags
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.version = function(str, flags) {
+ if (0 == arguments.length) return this._version;
+ this._version = str;
+ flags = flags || '-V, --version';
+ this.option(flags, 'output the version number');
+ this.on('version', function() {
+ process.stdout.write(str + '\n');
+ process.exit(0);
+ });
+ return this;
+};
+
+/**
+ * Set the description to `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.description = function(str) {
+ if (0 == arguments.length) return this._description;
+ this._description = str;
+ return this;
+};
+
+/**
+ * Set an alias for the command
+ *
+ * @param {String} alias
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.alias = function(alias) {
+ if (0 == arguments.length) return this._alias;
+ this._alias = alias;
+ return this;
+};
+
+/**
+ * Set / get the command usage `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.usage = function(str) {
+ var args = this._args.map(function(arg) {
+ return humanReadableArgName(arg);
+ });
+
+ var usage = '[options]'
+ + (this.commands.length ? ' [command]' : '')
+ + (this._args.length ? ' ' + args.join(' ') : '');
+
+ if (0 == arguments.length) return this._usage || usage;
+ this._usage = str;
+
+ return this;
+};
+
+/**
+ * Get the name of the command
+ *
+ * @param {String} name
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.name = function() {
+ return this._name;
+};
+
+/**
+ * Return the largest option length.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+Command.prototype.largestOptionLength = function() {
+ return this.options.reduce(function(max, option) {
+ return Math.max(max, option.flags.length);
+ }, 0);
+};
+
+/**
+ * Return help for options.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.optionHelp = function() {
+ var width = this.largestOptionLength();
+
+ // Prepend the help information
+ return [pad('-h, --help', width) + ' ' + 'output usage information']
+ .concat(this.options.map(function(option) {
+ return pad(option.flags, width) + ' ' + option.description;
+ }))
+ .join('\n');
+};
+
+/**
+ * Return command help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.commandHelp = function() {
+ if (!this.commands.length) return '';
+
+ var commands = this.commands.filter(function(cmd) {
+ return !cmd._noHelp;
+ }).map(function(cmd) {
+ var args = cmd._args.map(function(arg) {
+ return humanReadableArgName(arg);
+ }).join(' ');
+
+ return [
+ cmd._name
+ + (cmd._alias
+ ? '|' + cmd._alias
+ : '')
+ + (cmd.options.length
+ ? ' [options]'
+ : '')
+ + ' ' + args
+ , cmd.description()
+ ];
+ });
+
+ var width = commands.reduce(function(max, command) {
+ return Math.max(max, command[0].length);
+ }, 0);
+
+ return [
+ ''
+ , ' Commands:'
+ , ''
+ , commands.map(function(cmd) {
+ return pad(cmd[0], width) + ' ' + cmd[1];
+ }).join('\n').replace(/^/gm, ' ')
+ , ''
+ ].join('\n');
+};
+
+/**
+ * Return program help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.helpInformation = function() {
+ var desc = [];
+ if (this._description) {
+ desc = [
+ ' ' + this._description
+ , ''
+ ];
+ }
+
+ var cmdName = this._name;
+ if (this._alias) {
+ cmdName = cmdName + '|' + this._alias;
+ }
+ var usage = [
+ ''
+ ,' Usage: ' + cmdName + ' ' + this.usage()
+ , ''
+ ];
+
+ var cmds = [];
+ var commandHelp = this.commandHelp();
+ if (commandHelp) cmds = [commandHelp];
+
+ var options = [
+ ' Options:'
+ , ''
+ , '' + this.optionHelp().replace(/^/gm, ' ')
+ , ''
+ , ''
+ ];
+
+ return usage
+ .concat(cmds)
+ .concat(desc)
+ .concat(options)
+ .join('\n');
+};
+
+/**
+ * Output help information for this command
+ *
+ * @api public
+ */
+
+Command.prototype.outputHelp = function() {
+ process.stdout.write(this.helpInformation());
+ this.emit('--help');
+};
+
+/**
+ * Output help information and exit.
+ *
+ * @api public
+ */
+
+Command.prototype.help = function() {
+ this.outputHelp();
+ process.exit();
+};
+
+/**
+ * Camel-case the given `flag`
+ *
+ * @param {String} flag
+ * @return {String}
+ * @api private
+ */
+
+function camelcase(flag) {
+ return flag.split('-').reduce(function(str, word) {
+ return str + word[0].toUpperCase() + word.slice(1);
+ });
+}
+
+/**
+ * Pad `str` to `width`.
+ *
+ * @param {String} str
+ * @param {Number} width
+ * @return {String}
+ * @api private
+ */
+
+function pad(str, width) {
+ var len = Math.max(0, width - str.length);
+ return str + Array(len + 1).join(' ');
+}
+
+/**
+ * Output help information if necessary
+ *
+ * @param {Command} command to output help for
+ * @param {Array} array of options to search for -h or --help
+ * @api private
+ */
+
+function outputHelpIfNecessary(cmd, options) {
+ options = options || [];
+ for (var i = 0; i < options.length; i++) {
+ if (options[i] == '--help' || options[i] == '-h') {
+ cmd.outputHelp();
+ process.exit(0);
+ }
+ }
+}
+
+/**
+ * Takes an argument an returns its human readable equivalent for help usage.
+ *
+ * @param {Object} arg
+ * @return {String}
+ * @api private
+ */
+
+function humanReadableArgName(arg) {
+ var nameOutput = arg.name + (arg.variadic === true ? '...' : '');
+
+ return arg.required
+ ? '<' + nameOutput + '>'
+ : '[' + nameOutput + ']'
+}
+
+// for versions before node v0.8 when there weren't `fs.existsSync`
+function exists(file) {
+ try {
+ if (fs.statSync(file).isFile()) {
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+}
+
--- /dev/null
+.idea/
+.DS_Store
+node_modules/
--- /dev/null
+language: node_js
+node_js:
+ - "0.10"
+ - "0.12"
+ - "io.js"
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2015 Zhiye Li
+
+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.
+
--- /dev/null
+# graceful-readlink
+[](https://www.npmjs.org/package/graceful-readlink)
+[](https://www.npmjs.org/package/graceful-readlink)
+
+
+## Usage
+
+```js
+var readlinkSync = require('graceful-readlink').readlinkSync;
+console.log(readlinkSync(f));
+// output
+// the file pointed to when `f` is a symbolic link
+// the `f` itself when `f` is not a symbolic link
+```
+## Licence
+
+MIT License
--- /dev/null
+var fs = require('fs')
+ , lstat = fs.lstatSync;
+
+exports.readlinkSync = function (p) {
+ if (lstat(p).isSymbolicLink()) {
+ return fs.readlinkSync(p);
+ } else {
+ return p;
+ }
+};
+
+
--- /dev/null
+{
+ "name": "graceful-readlink",
+ "version": "1.0.1",
+ "description": "graceful fs.readlink",
+ "main": "index.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/zhiyelee/graceful-readlink.git"
+ },
+ "homepage": "https://github.com/zhiyelee/graceful-readlink",
+ "bugs": {
+ "url": "https://github.com/zhiyelee/graceful-readlink/issues"
+ },
+ "keywords": [
+ "fs.readlink",
+ "readlink"
+ ],
+ "author": {
+ "name": "zhiyelee"
+ },
+ "license": "MIT",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "gitHead": "f6655275bebef706fb63fd01b5f062a7052419a5",
+ "_id": "graceful-readlink@1.0.1",
+ "_shasum": "4cafad76bc62f02fa039b2f94e9a3dd3a391a725",
+ "_from": "graceful-readlink@>=1.0.0",
+ "_npmVersion": "2.1.17",
+ "_nodeVersion": "0.11.14",
+ "_npmUser": {
+ "name": "zhiyelee",
+ "email": "zhiyelee@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "zhiyelee",
+ "email": "zhiyelee@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "4cafad76bc62f02fa039b2f94e9a3dd3a391a725",
+ "tarball": "http://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
+}
--- /dev/null
+{
+ "name": "commander",
+ "version": "2.8.1",
+ "description": "the complete solution for node.js command-line programs",
+ "keywords": [
+ "command",
+ "option",
+ "parser"
+ ],
+ "author": {
+ "name": "TJ Holowaychuk",
+ "email": "tj@vision-media.ca"
+ },
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tj/commander.js.git"
+ },
+ "devDependencies": {
+ "should": ">= 0.0.1",
+ "sinon": ">= 1.14.1"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "main": "index",
+ "engines": {
+ "node": ">= 0.6.x"
+ },
+ "files": [
+ "index.js"
+ ],
+ "dependencies": {
+ "graceful-readlink": ">= 1.0.0"
+ },
+ "gitHead": "c6c84726050b19c0373de27cd359f3baddff579f",
+ "bugs": {
+ "url": "https://github.com/tj/commander.js/issues"
+ },
+ "homepage": "https://github.com/tj/commander.js",
+ "_id": "commander@2.8.1",
+ "_shasum": "06be367febfda0c330aa1e2a072d3dc9762425d4",
+ "_from": "commander@>=2.8.1 <3.0.0",
+ "_npmVersion": "2.5.1",
+ "_nodeVersion": "0.12.0",
+ "_npmUser": {
+ "name": "zhiyelee",
+ "email": "zhiyelee@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "tjholowaychuk",
+ "email": "tj@vision-media.ca"
+ },
+ {
+ "name": "somekittens",
+ "email": "rkoutnik@gmail.com"
+ },
+ {
+ "name": "zhiyelee",
+ "email": "zhiyelee@gmail.com"
+ },
+ {
+ "name": "thethomaseffect",
+ "email": "thethomaseffect@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "06be367febfda0c330aa1e2a072d3dc9762425d4",
+ "tarball": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz"
+}
--- /dev/null
+node_modules
+cosmicrealms.com
--- /dev/null
+language: node_js
+node_js:
+ - "0.10"
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2014 Mathias Buus
+
+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.
\ No newline at end of file
--- /dev/null
+# is-my-json-valid
+
+A [JSONSchema](http://json-schema.org/) validator that uses code generation
+to be extremely fast
+
+```
+npm install is-my-json-valid
+```
+
+It passes the entire JSONSchema v4 test suite except for `remoteRefs` and `maxLength`/`minLength` when using unicode surrogate pairs.
+
+[](http://travis-ci.org/mafintosh/is-my-json-valid)
+
+## Usage
+
+Simply pass a schema to compile it
+
+``` js
+var validator = require('is-my-json-valid')
+
+var validate = validator({
+ required: true,
+ type: 'object',
+ properties: {
+ hello: {
+ required: true,
+ type: 'string'
+ }
+ }
+})
+
+console.log('should be valid', validate({hello: 'world'}))
+console.log('should not be valid', validate({}))
+
+// get the last list of errors by checking validate.errors
+// the following will print [{field: 'data.hello', message: 'is required'}]
+console.log(validate.errors)
+```
+
+You can also pass the schema as a string
+
+``` js
+var validate = validate('{"type": ... }')
+```
+
+Optionally you can use the require submodule to load a schema from `__dirname`
+
+``` js
+var validator = require('is-my-json-valid/require')
+var validate = validator('my-schema.json')
+```
+
+## Custom formats
+
+is-my-json-valid supports the formats specified in JSON schema v4 (such as date-time).
+If you want to add your own custom formats pass them as the formats options to the validator
+
+``` js
+var validate = validator({
+ type: 'string',
+ required: true,
+ format: 'only-a'
+}, {
+ formats: {
+ 'only-a': /^a+$/
+ }
+})
+
+console.log(validate('aa')) // true
+console.log(validate('ab')) // false
+```
+
+## External schemas
+
+You can pass in external schemas that you reference using the `$ref` attribute as the `schemas` option
+
+``` js
+var ext = {
+ required: true,
+ type: 'string'
+}
+
+var schema = {
+ $ref: '#ext' // references another schema called ext
+}
+
+// pass the external schemas as an option
+var validate = validate(schema, {schemas: {ext: ext}})
+
+validate('hello') // returns true
+validate(42) // return false
+```
+
+## Filtering away additional properties
+
+is-my-json-valid supports filtering away properties not in the schema
+
+``` js
+var filter = validator.filter({
+ required: true,
+ type: 'object',
+ properties: {
+ hello: {type: 'string', required: true}
+ },
+ additionalProperties: false
+})
+
+var doc = {hello: 'world', notInSchema: true}
+console.log(filter(doc)) // {hello: 'world'}
+```
+
+## Verbose mode outputs the value on errors
+
+is-my-json-valid outputs the value causing an error when verbose is set to true
+
+``` js
+var validate = validator({
+ required: true,
+ type: 'object',
+ properties: {
+ hello: {
+ required: true,
+ type: 'string'
+ }
+ }
+}, {
+ verbose: true
+})
+
+validate({hello: 100});
+console.log(validate.errors) // {field: 'data.hello', message: 'is the wrong type', value: 100}
+```
+
+## Greedy mode tries to validate as much as possible
+
+By default is-my-json-valid bails on first validation error but when greedy is
+set to true it tries to validate as much as possible:
+
+``` js
+var validate = validator({
+ type: 'object',
+ properties: {
+ x: {
+ type: 'number'
+ }
+ },
+ required: ['x', 'y']
+}, {
+ greedy: true
+});
+
+validate({x: 'string'});
+console.log(validate.errors) // [{field: 'data.y', message: 'is required'},
+ // {field: 'data.x', message: 'is the wrong type'}]
+```
+
+## Performance
+
+is-my-json-valid uses code generation to turn your JSON schema into basic javascript code that is easily optimizeable by v8.
+
+At the time of writing, is-my-json-valid is the __fastest validator__ when running
+
+* [json-schema-benchmark](https://github.com/Muscula/json-schema-benchmark)
+* [cosmicreals.com benchmark](http://cosmicrealms.com/blog/2014/08/29/benchmark-of-node-dot-js-json-validation-modules-part-3/)
+* [jsck benchmark](https://github.com/pandastrike/jsck/issues/72#issuecomment-70992684)
+* [themis benchmark](https://cdn.rawgit.com/playlyfe/themis/master/benchmark/results.html)
+* [z-schema benchmark](https://rawgit.com/zaggino/z-schema/master/benchmark/results.html)
+
+If you know any other relevant benchmarks open a PR and I'll add them.
+
+## License
+
+MIT
--- /dev/null
+var validator = require('./')
+
+var validate = validator({
+ type: 'object',
+ properties: {
+ hello: {
+ required: true,
+ type: 'string'
+ }
+ }
+})
+
+console.log('should be valid', validate({hello: 'world'}))
+console.log('should not be valid', validate({}))
+
+// get the last error message by checking validate.error
+// the following will print "data.hello is required"
+console.log('the errors were:', validate.errors)
--- /dev/null
+exports['date-time'] = /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}[tT ]\d{2}:\d{2}:\d{2}(\.\d+)?([zZ]|[+-]\d{2}:\d{2})$/
+exports['date'] = /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}$/
+exports['time'] = /^\d{2}:\d{2}:\d{2}$/
+exports['email'] = /^\S+@\S+$/
+exports['ip-address'] = exports['ipv4'] = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
+exports['ipv6'] = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
+exports['uri'] = /^[a-zA-Z][a-zA-Z0-9+-.]*:[^\s]*$/
+exports['color'] = /(#?([0-9A-Fa-f]{3,6})\b)|(aqua)|(black)|(blue)|(fuchsia)|(gray)|(green)|(lime)|(maroon)|(navy)|(olive)|(orange)|(purple)|(red)|(silver)|(teal)|(white)|(yellow)|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\))/
+exports['hostname'] = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$/
+exports['alpha'] = /^[a-zA-Z]+$/
+exports['alphanumeric'] = /^[a-zA-Z0-9]+$/
+exports['style'] = /\s*(.+?):\s*([^;]+);?/g
+exports['phone'] = /^\+(?:[0-9] ?){6,14}[0-9]$/
+exports['utc-millisec'] = /^[0-9]+(\.?[0-9]+)?$/
--- /dev/null
+var genobj = require('generate-object-property')
+var genfun = require('generate-function')
+var jsonpointer = require('jsonpointer')
+var xtend = require('xtend')
+var formats = require('./formats')
+
+var get = function(obj, additionalSchemas, ptr) {
+ if (/^https?:\/\//.test(ptr)) return null
+
+ var visit = function(sub) {
+ if (sub && sub.id === ptr) return sub
+ if (typeof sub !== 'object' || !sub) return null
+ return Object.keys(sub).reduce(function(res, k) {
+ return res || visit(sub[k])
+ }, null)
+ }
+
+ var res = visit(obj)
+ if (res) return res
+
+ ptr = ptr.replace(/^#/, '')
+ ptr = ptr.replace(/\/$/, '')
+
+ try {
+ return jsonpointer.get(obj, decodeURI(ptr))
+ } catch (err) {
+ var end = ptr.indexOf('#')
+ var other
+ // external reference
+ if (end !== 0) {
+ // fragment doesn't exist.
+ if (end === -1) {
+ other = additionalSchemas[ptr]
+ } else {
+ var ext = ptr.slice(0, end)
+ other = additionalSchemas[ext]
+ var fragment = ptr.slice(end).replace(/^#/, '')
+ try {
+ return jsonpointer.get(other, fragment)
+ } catch (err) {}
+ }
+ } else {
+ other = additionalSchemas[ptr]
+ }
+ return other || null
+ }
+}
+
+var formatName = function(field) {
+ field = JSON.stringify(field)
+ var pattern = /\[([^\[\]"]+)\]/
+ while (pattern.test(field)) field = field.replace(pattern, '."+$1+"')
+ return field
+}
+
+var types = {}
+
+types.any = function() {
+ return 'true'
+}
+
+types.null = function(name) {
+ return name+' === null'
+}
+
+types.boolean = function(name) {
+ return 'typeof '+name+' === "boolean"'
+}
+
+types.array = function(name) {
+ return 'Array.isArray('+name+')'
+}
+
+types.object = function(name) {
+ return 'typeof '+name+' === "object" && '+name+' && !Array.isArray('+name+')'
+}
+
+types.number = function(name) {
+ return 'typeof '+name+' === "number"'
+}
+
+types.integer = function(name) {
+ return 'typeof '+name+' === "number" && (Math.floor('+name+') === '+name+' || '+name+' > 9007199254740992 || '+name+' < -9007199254740992)'
+}
+
+types.string = function(name) {
+ return 'typeof '+name+' === "string"'
+}
+
+var unique = function(array) {
+ var list = []
+ for (var i = 0; i < array.length; i++) {
+ list.push(typeof array[i] === 'object' ? JSON.stringify(array[i]) : array[i])
+ }
+ for (var i = 1; i < list.length; i++) {
+ if (list.indexOf(list[i]) !== i) return false
+ }
+ return true
+}
+
+var toType = function(node) {
+ return node.type
+}
+
+var compile = function(schema, cache, root, reporter, opts) {
+ var fmts = opts ? xtend(formats, opts.formats) : formats
+ var scope = {unique:unique, formats:fmts}
+ var verbose = opts ? !!opts.verbose : false;
+ var greedy = opts && opts.greedy !== undefined ?
+ opts.greedy : false;
+
+ var syms = {}
+ var gensym = function(name) {
+ return name+(syms[name] = (syms[name] || 0)+1)
+ }
+
+ var reversePatterns = {}
+ var patterns = function(p) {
+ if (reversePatterns[p]) return reversePatterns[p]
+ var n = gensym('pattern')
+ scope[n] = new RegExp(p)
+ reversePatterns[p] = n
+ return n
+ }
+
+ var vars = ['i','j','k','l','m','n','o','p','q','r','s','t','u','v','x','y','z']
+ var genloop = function() {
+ var v = vars.shift()
+ vars.push(v+v[0])
+ return v
+ }
+
+ var visit = function(name, node, reporter, filter) {
+ var properties = node.properties
+ var type = node.type
+ var tuple = false
+
+ if (Array.isArray(node.items)) { // tuple type
+ properties = {}
+ node.items.forEach(function(item, i) {
+ properties[i] = item
+ })
+ type = 'array'
+ tuple = true
+ }
+
+ var indent = 0
+ var error = function(msg, prop, value) {
+ validate('errors++')
+ if (reporter === true) {
+ validate('if (validate.errors === null) validate.errors = []')
+ if (verbose) {
+ validate('validate.errors.push({field:%s,message:%s,value:%s})', formatName(prop || name), JSON.stringify(msg), value || name)
+ } else {
+ validate('validate.errors.push({field:%s,message:%s})', formatName(prop || name), JSON.stringify(msg))
+ }
+ }
+ }
+
+ if (node.required === true) {
+ indent++
+ validate('if (%s === undefined) {', name)
+ error('is required')
+ validate('} else {')
+ } else {
+ indent++
+ validate('if (%s !== undefined) {', name)
+ }
+
+ var valid = [].concat(type)
+ .map(function(t) {
+ return types[t || 'any'](name)
+ })
+ .join(' || ') || 'true'
+
+ if (valid !== 'true') {
+ indent++
+ validate('if (!(%s)) {', valid)
+ error('is the wrong type')
+ validate('} else {')
+ }
+
+ if (tuple) {
+ if (node.additionalItems === false) {
+ validate('if (%s.length > %d) {', name, node.items.length)
+ error('has additional items')
+ validate('}')
+ } else if (node.additionalItems) {
+ var i = genloop()
+ validate('for (var %s = %d; %s < %s.length; %s++) {', i, node.items.length, i, name, i)
+ visit(name+'['+i+']', node.additionalItems, reporter, filter)
+ validate('}')
+ }
+ }
+
+ if (node.format && fmts[node.format]) {
+ if (type !== 'string' && formats[node.format]) validate('if (%s) {', types.string(name))
+ var n = gensym('format')
+ scope[n] = fmts[node.format]
+
+ if (typeof scope[n] === 'function') validate('if (!%s(%s)) {', n, name)
+ else validate('if (!%s.test(%s)) {', n, name)
+ error('must be '+node.format+' format')
+ validate('}')
+ if (type !== 'string' && formats[node.format]) validate('}')
+ }
+
+ if (Array.isArray(node.required)) {
+ var isUndefined = function(req) {
+ return genobj(name, req) + ' === undefined'
+ }
+
+ var checkRequired = function (req) {
+ var prop = genobj(name, req);
+ validate('if (%s === undefined) {', prop)
+ error('is required', prop)
+ validate('missing++')
+ validate('}')
+ }
+ validate('if ((%s)) {', type !== 'object' ? types.object(name) : 'true')
+ validate('var missing = 0')
+ node.required.map(checkRequired)
+ validate('}');
+ if (!greedy) {
+ validate('if (missing === 0) {')
+ indent++
+ }
+ }
+
+ if (node.uniqueItems) {
+ if (type !== 'array') validate('if (%s) {', types.array(name))
+ validate('if (!(unique(%s))) {', name)
+ error('must be unique')
+ validate('}')
+ if (type !== 'array') validate('}')
+ }
+
+ if (node.enum) {
+ var complex = node.enum.some(function(e) {
+ return typeof e === 'object'
+ })
+
+ var compare = complex ?
+ function(e) {
+ return 'JSON.stringify('+name+')'+' !== JSON.stringify('+JSON.stringify(e)+')'
+ } :
+ function(e) {
+ return name+' !== '+JSON.stringify(e)
+ }
+
+ validate('if (%s) {', node.enum.map(compare).join(' && ') || 'false')
+ error('must be an enum value')
+ validate('}')
+ }
+
+ if (node.dependencies) {
+ if (type !== 'object') validate('if (%s) {', types.object(name))
+
+ Object.keys(node.dependencies).forEach(function(key) {
+ var deps = node.dependencies[key]
+ if (typeof deps === 'string') deps = [deps]
+
+ var exists = function(k) {
+ return genobj(name, k) + ' !== undefined'
+ }
+
+ if (Array.isArray(deps)) {
+ validate('if (%s !== undefined && !(%s)) {', genobj(name, key), deps.map(exists).join(' && ') || 'true')
+ error('dependencies not set')
+ validate('}')
+ }
+ if (typeof deps === 'object') {
+ validate('if (%s !== undefined) {', genobj(name, key))
+ visit(name, deps, reporter, filter)
+ validate('}')
+ }
+ })
+
+ if (type !== 'object') validate('}')
+ }
+
+ if (node.additionalProperties || node.additionalProperties === false) {
+ if (type !== 'object') validate('if (%s) {', types.object(name))
+
+ var i = genloop()
+ var keys = gensym('keys')
+
+ var toCompare = function(p) {
+ return keys+'['+i+'] !== '+JSON.stringify(p)
+ }
+
+ var toTest = function(p) {
+ return '!'+patterns(p)+'.test('+keys+'['+i+'])'
+ }
+
+ var additionalProp = Object.keys(properties || {}).map(toCompare)
+ .concat(Object.keys(node.patternProperties || {}).map(toTest))
+ .join(' && ') || 'true'
+
+ validate('var %s = Object.keys(%s)', keys, name)
+ ('for (var %s = 0; %s < %s.length; %s++) {', i, i, keys, i)
+ ('if (%s) {', additionalProp)
+
+ if (node.additionalProperties === false) {
+ if (filter) validate('delete %s', name+'['+keys+'['+i+']]')
+ error('has additional properties', null, JSON.stringify(name+'.') + ' + ' + keys + '['+i+']')
+ } else {
+ visit(name+'['+keys+'['+i+']]', node.additionalProperties, reporter, filter)
+ }
+
+ validate
+ ('}')
+ ('}')
+
+ if (type !== 'object') validate('}')
+ }
+
+ if (node.$ref) {
+ var sub = get(root, opts && opts.schemas || {}, node.$ref)
+ if (sub) {
+ var fn = cache[node.$ref]
+ if (!fn) {
+ cache[node.$ref] = function proxy(data) {
+ return fn(data)
+ }
+ fn = compile(sub, cache, root, false, opts)
+ }
+ var n = gensym('ref')
+ scope[n] = fn
+ validate('if (!(%s(%s))) {', n, name)
+ error('referenced schema does not match')
+ validate('}')
+ }
+ }
+
+ if (node.not) {
+ var prev = gensym('prev')
+ validate('var %s = errors', prev)
+ visit(name, node.not, false, filter)
+ validate('if (%s === errors) {', prev)
+ error('negative schema matches')
+ validate('} else {')
+ ('errors = %s', prev)
+ ('}')
+ }
+
+ if (node.items && !tuple) {
+ if (type !== 'array') validate('if (%s) {', types.array(name))
+
+ var i = genloop()
+ validate('for (var %s = 0; %s < %s.length; %s++) {', i, i, name, i)
+ visit(name+'['+i+']', node.items, reporter, filter)
+ validate('}')
+
+ if (type !== 'array') validate('}')
+ }
+
+ if (node.patternProperties) {
+ if (type !== 'object') validate('if (%s) {', types.object(name))
+ var keys = gensym('keys')
+ var i = genloop()
+ validate
+ ('var %s = Object.keys(%s)', keys, name)
+ ('for (var %s = 0; %s < %s.length; %s++) {', i, i, keys, i)
+
+ Object.keys(node.patternProperties).forEach(function(key) {
+ var p = patterns(key)
+ validate('if (%s.test(%s)) {', p, keys+'['+i+']')
+ visit(name+'['+keys+'['+i+']]', node.patternProperties[key], reporter, filter)
+ validate('}')
+ })
+
+ validate('}')
+ if (type !== 'object') validate('}')
+ }
+
+ if (node.pattern) {
+ var p = patterns(node.pattern)
+ if (type !== 'string') validate('if (%s) {', types.string(name))
+ validate('if (!(%s.test(%s))) {', p, name)
+ error('pattern mismatch')
+ validate('}')
+ if (type !== 'string') validate('}')
+ }
+
+ if (node.allOf) {
+ node.allOf.forEach(function(sch) {
+ visit(name, sch, reporter, filter)
+ })
+ }
+
+ if (node.anyOf && node.anyOf.length) {
+ var prev = gensym('prev')
+
+ node.anyOf.forEach(function(sch, i) {
+ if (i === 0) {
+ validate('var %s = errors', prev)
+ } else {
+ validate('if (errors !== %s) {', prev)
+ ('errors = %s', prev)
+ }
+ visit(name, sch, false, false)
+ })
+ node.anyOf.forEach(function(sch, i) {
+ if (i) validate('}')
+ })
+ validate('if (%s !== errors) {', prev)
+ error('no schemas match')
+ validate('}')
+ }
+
+ if (node.oneOf && node.oneOf.length) {
+ var prev = gensym('prev')
+ var passes = gensym('passes')
+
+ validate
+ ('var %s = errors', prev)
+ ('var %s = 0', passes)
+
+ node.oneOf.forEach(function(sch, i) {
+ visit(name, sch, false, false)
+ validate('if (%s === errors) {', prev)
+ ('%s++', passes)
+ ('} else {')
+ ('errors = %s', prev)
+ ('}')
+ })
+
+ validate('if (%s !== 1) {', passes)
+ error('no (or more than one) schemas match')
+ validate('}')
+ }
+
+ if (node.multipleOf !== undefined) {
+ if (type !== 'number' && type !== 'integer') validate('if (%s) {', types.number(name))
+
+ var factor = ((node.multipleOf | 0) !== node.multipleOf) ? Math.pow(10, node.multipleOf.toString().split('.').pop().length) : 1
+ if (factor > 1) validate('if ((%d*%s) % %d) {', factor, name, factor*node.multipleOf)
+ else validate('if (%s % %d) {', name, node.multipleOf)
+
+ error('has a remainder')
+ validate('}')
+
+ if (type !== 'number' && type !== 'integer') validate('}')
+ }
+
+ if (node.maxProperties !== undefined) {
+ if (type !== 'object') validate('if (%s) {', types.object(name))
+
+ validate('if (Object.keys(%s).length > %d) {', name, node.maxProperties)
+ error('has more properties than allowed')
+ validate('}')
+
+ if (type !== 'object') validate('}')
+ }
+
+ if (node.minProperties !== undefined) {
+ if (type !== 'object') validate('if (%s) {', types.object(name))
+
+ validate('if (Object.keys(%s).length < %d) {', name, node.minProperties)
+ error('has less properties than allowed')
+ validate('}')
+
+ if (type !== 'object') validate('}')
+ }
+
+ if (node.maxItems !== undefined) {
+ if (type !== 'array') validate('if (%s) {', types.array(name))
+
+ validate('if (%s.length > %d) {', name, node.maxItems)
+ error('has more items than allowed')
+ validate('}')
+
+ if (type !== 'array') validate('}')
+ }
+
+ if (node.minItems !== undefined) {
+ if (type !== 'array') validate('if (%s) {', types.array(name))
+
+ validate('if (%s.length < %d) {', name, node.minItems)
+ error('has less items than allowed')
+ validate('}')
+
+ if (type !== 'array') validate('}')
+ }
+
+ if (node.maxLength !== undefined) {
+ if (type !== 'string') validate('if (%s) {', types.string(name))
+
+ validate('if (%s.length > %d) {', name, node.maxLength)
+ error('has longer length than allowed')
+ validate('}')
+
+ if (type !== 'string') validate('}')
+ }
+
+ if (node.minLength !== undefined) {
+ if (type !== 'string') validate('if (%s) {', types.string(name))
+
+ validate('if (%s.length < %d) {', name, node.minLength)
+ error('has less length than allowed')
+ validate('}')
+
+ if (type !== 'string') validate('}')
+ }
+
+ if (node.minimum !== undefined) {
+ validate('if (%s %s %d) {', name, node.exclusiveMinimum ? '<=' : '<', node.minimum)
+ error('is less than minimum')
+ validate('}')
+ }
+
+ if (node.maximum !== undefined) {
+ validate('if (%s %s %d) {', name, node.exclusiveMaximum ? '>=' : '>', node.maximum)
+ error('is more than maximum')
+ validate('}')
+ }
+
+ if (properties) {
+ Object.keys(properties).forEach(function(p) {
+ visit(genobj(name, p), properties[p], reporter, filter)
+ })
+ }
+
+ while (indent--) validate('}')
+ }
+
+ var validate = genfun
+ ('function validate(data) {')
+ ('validate.errors = null')
+ ('var errors = 0')
+
+ visit('data', schema, reporter, opts && opts.filter)
+
+ validate
+ ('return errors === 0')
+ ('}')
+
+ validate = validate.toFunction(scope)
+ validate.errors = null
+
+ validate.__defineGetter__('error', function() {
+ if (!validate.errors) return ''
+ return validate.errors
+ .map(function(err) {
+ return err.field+' '+err.message
+ })
+ .join('\n')
+ })
+
+ validate.toJSON = function() {
+ return schema
+ }
+
+ return validate
+}
+
+module.exports = function(schema, opts) {
+ if (typeof schema === 'string') schema = JSON.parse(schema)
+ return compile(schema, {}, schema, true, opts)
+}
+
+module.exports.filter = function(schema, opts) {
+ var validate = module.exports(schema, xtend(opts, {filter: true}))
+ return function(sch) {
+ validate(sch)
+ return sch
+ }
+}
--- /dev/null
+node_modules
--- /dev/null
+language: node_js
+node_js:
+ - "0.10"
--- /dev/null
+# generate-function
+
+Module that helps you write generated functions in Node
+
+```
+npm install generate-function
+```
+
+[](http://travis-ci.org/mafintosh/generate-function)
+
+## Disclamer
+
+Writing code that generates code is hard.
+You should only use this if you really, really, really need this for performance reasons (like schema validators / parsers etc).
+
+## Usage
+
+``` js
+var genfun = require('generate-function')
+
+var addNumber = function(val) {
+ var fn = genfun()
+ ('function add(n) {')
+ ('return n + %d', val) // supports format strings to insert values
+ ('}')
+
+ return fn.toFunction() // will compile the function
+}
+
+var add2 = addNumber(2)
+
+console.log('1+2=', add2(1))
+console.log(add2.toString()) // prints the generated function
+```
+
+If you need to close over variables in your generated function pass them to `toFunction(scope)`
+
+``` js
+var multiply = function(a, b) {
+ return a * b
+}
+
+var addAndMultiplyNumber = function(val) {
+ var fn = genfun()
+ ('function(n) {')
+ ('if (typeof n !== "number") {') // ending a line with { will indent the source
+ ('throw new Error("argument should be a number")')
+ ('}')
+ ('var result = multiply(%d, n+%d)', val, val)
+ ('return result')
+ ('}')
+
+ // use fn.toString() if you want to see the generated source
+
+ return fn.toFunction({
+ multiply: multiply
+ })
+}
+
+var addAndMultiply2 = addAndMultiplyNumber(2)
+
+console.log('(3 + 2) * 2 =', addAndMultiply2(3))
+```
+
+## Related
+
+See [generate-object-property](https://github.com/mafintosh/generate-object-property) if you need to safely generate code that
+can be used to reference an object property
+
+## License
+
+MIT
\ No newline at end of file
--- /dev/null
+var genfun = require('./')
+
+var multiply = function(a, b) {
+ return a * b
+}
+
+var addAndMultiplyNumber = function(val) {
+ var fn = genfun()
+ ('function(n) {')
+ ('if (typeof n !== "number") {') // ending a line with { will indent the source
+ ('throw new Error("argument should be a number")')
+ ('}')
+ ('var result = multiply(%d, n+%d)', val, val)
+ ('return result')
+ ('}')
+
+ // use fn.toString() if you want to see the generated source
+
+ return fn.toFunction({
+ multiply: multiply
+ })
+}
+
+var addAndMultiply2 = addAndMultiplyNumber(2)
+
+console.log(addAndMultiply2.toString())
+console.log('(3 + 2) * 2 =', addAndMultiply2(3))
\ No newline at end of file
--- /dev/null
+var util = require('util')
+
+var INDENT_START = /[\{\[]/
+var INDENT_END = /[\}\]]/
+
+module.exports = function() {
+ var lines = []
+ var indent = 0
+
+ var push = function(str) {
+ var spaces = ''
+ while (spaces.length < indent*2) spaces += ' '
+ lines.push(spaces+str)
+ }
+
+ var line = function(fmt) {
+ if (!fmt) return line
+
+ if (INDENT_END.test(fmt.trim()[0]) && INDENT_START.test(fmt[fmt.length-1])) {
+ indent--
+ push(util.format.apply(util, arguments))
+ indent++
+ return line
+ }
+ if (INDENT_START.test(fmt[fmt.length-1])) {
+ push(util.format.apply(util, arguments))
+ indent++
+ return line
+ }
+ if (INDENT_END.test(fmt.trim()[0])) {
+ indent--
+ push(util.format.apply(util, arguments))
+ return line
+ }
+
+ push(util.format.apply(util, arguments))
+ return line
+ }
+
+ line.toString = function() {
+ return lines.join('\n')
+ }
+
+ line.toFunction = function(scope) {
+ var src = 'return ('+line.toString()+')'
+
+ var keys = Object.keys(scope || {}).map(function(key) {
+ return key
+ })
+
+ var vals = keys.map(function(key) {
+ return scope[key]
+ })
+
+ return Function.apply(null, keys.concat(src)).apply(null, vals)
+ }
+
+ if (arguments.length) line.apply(null, arguments)
+
+ return line
+}
--- /dev/null
+{
+ "name": "generate-function",
+ "version": "2.0.0",
+ "description": "Module that helps you write generated functions in Node",
+ "main": "index.js",
+ "scripts": {
+ "test": "tape test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mafintosh/generate-function"
+ },
+ "keywords": [
+ "generate",
+ "code",
+ "generation",
+ "function",
+ "performance"
+ ],
+ "author": {
+ "name": "Mathias Buus"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/mafintosh/generate-function/issues"
+ },
+ "homepage": "https://github.com/mafintosh/generate-function",
+ "devDependencies": {
+ "tape": "^2.13.4"
+ },
+ "gitHead": "3d5fc8de5859be95f58e3af9bfb5f663edd95149",
+ "_id": "generate-function@2.0.0",
+ "_shasum": "6858fe7c0969b7d4e9093337647ac79f60dfbe74",
+ "_from": "generate-function@>=2.0.0 <3.0.0",
+ "_npmVersion": "1.4.23",
+ "_npmUser": {
+ "name": "mafintosh",
+ "email": "mathiasbuus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mafintosh",
+ "email": "mathiasbuus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "6858fe7c0969b7d4e9093337647ac79f60dfbe74",
+ "tarball": "http://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
+}
--- /dev/null
+var tape = require('tape')
+var genfun = require('./')
+
+tape('generate add function', function(t) {
+ var fn = genfun()
+ ('function add(n) {')
+ ('return n + %d', 42)
+ ('}')
+
+ t.same(fn.toString(), 'function add(n) {\n return n + 42\n}', 'code is indented')
+ t.same(fn.toFunction()(10), 52, 'function works')
+ t.end()
+})
+
+tape('generate function + closed variables', function(t) {
+ var fn = genfun()
+ ('function add(n) {')
+ ('return n + %d + number', 42)
+ ('}')
+
+ var notGood = fn.toFunction()
+ var good = fn.toFunction({number:10})
+
+ try {
+ notGood(10)
+ t.ok(false, 'function should not work')
+ } catch (err) {
+ t.same(err.message, 'number is not defined', 'throws reference error')
+ }
+
+ t.same(good(11), 63, 'function with closed var works')
+ t.end()
+})
\ No newline at end of file
--- /dev/null
+node_modules
--- /dev/null
+language: node_js
+node_js:
+ - "0.10"
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2014 Mathias Buus
+
+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.
\ No newline at end of file
--- /dev/null
+# generate-object-property
+
+Generate safe JS code that can used to reference a object property
+
+ npm install generate-object-property
+
+[](http://travis-ci.org/mafintosh/generate-object-property)
+
+## Usage
+
+``` js
+var gen = require('generate-object-property');
+console.log(gen('a','b')); // prints a.b
+console.log(gen('a', 'foo-bar')); // prints a["foo-bar"]
+```
+
+## License
+
+MIT
\ No newline at end of file
--- /dev/null
+var isProperty = require('is-property')
+
+var gen = function(obj, prop) {
+ return isProperty(prop) ? obj+'.'+prop : obj+'['+JSON.stringify(prop)+']'
+}
+
+gen.valid = isProperty
+gen.property = function (prop) {
+ return isProperty(prop) ? prop : JSON.stringify(prop)
+}
+
+module.exports = gen
--- /dev/null
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+npm-debug.log
+node_modules/*
+*.DS_Store
+test/*
\ No newline at end of file
--- /dev/null
+
+The MIT License (MIT)
+
+Copyright (c) 2013 Mikola Lysenko
+
+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.
--- /dev/null
+is-property
+===========
+Tests if a property of a JavaScript object can be accessed using the dot (.) notation or if it must be enclosed in brackets, (ie use x[" ... "])
+
+Example
+-------
+
+```javascript
+var isProperty = require("is-property")
+
+console.log(isProperty("foo")) //Prints true
+console.log(isProperty("0")) //Prints false
+```
+
+Install
+-------
+
+ npm install is-property
+
+### `require("is-property")(str)`
+Checks if str is a property
+
+* `str` is a string which we will test if it is a property or not
+
+**Returns** true or false depending if str is a property
+
+## Credits
+(c) 2013 Mikola Lysenko. MIT License
\ No newline at end of file
--- /dev/null
+"use strict"
+function isProperty(str) {
+ return /^[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc0-9\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19d9\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1dc0-\u1de6\u1dfc-\u1dff\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f]*$/.test(str)
+}
+module.exports = isProperty
\ No newline at end of file
--- /dev/null
+{
+ "name": "is-property",
+ "version": "1.0.2",
+ "description": "Tests if a JSON property can be accessed using . syntax",
+ "main": "is-property.js",
+ "directories": {
+ "test": "test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tape": "~1.0.4"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mikolalysenko/is-property.git"
+ },
+ "keywords": [
+ "is",
+ "property",
+ "json",
+ "dot",
+ "bracket",
+ ".",
+ "[]"
+ ],
+ "author": {
+ "name": "Mikola Lysenko"
+ },
+ "license": "MIT",
+ "gitHead": "0a85ea5b6b1264ea1cdecc6e5cf186adbb3ffc50",
+ "bugs": {
+ "url": "https://github.com/mikolalysenko/is-property/issues"
+ },
+ "homepage": "https://github.com/mikolalysenko/is-property",
+ "_id": "is-property@1.0.2",
+ "_shasum": "57fe1c4e48474edd65b09911f26b1cd4095dda84",
+ "_from": "is-property@>=1.0.0 <2.0.0",
+ "_npmVersion": "2.1.4",
+ "_nodeVersion": "0.10.26",
+ "_npmUser": {
+ "name": "mikolalysenko",
+ "email": "mikolalysenko@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikolalysenko",
+ "email": "mikolalysenko@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "57fe1c4e48474edd65b09911f26b1cd4095dda84",
+ "tarball": "http://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
+ },
+ "_resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
+}
--- /dev/null
+{
+ "name": "generate-object-property",
+ "version": "1.2.0",
+ "description": "Generate safe JS code that can used to reference a object property",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mafintosh/generate-object-property"
+ },
+ "devDependencies": {
+ "tape": "^2.13.0"
+ },
+ "scripts": {
+ "test": "tape test.js"
+ },
+ "dependencies": {
+ "is-property": "^1.0.0"
+ },
+ "bugs": {
+ "url": "https://github.com/mafintosh/generate-object-property/issues"
+ },
+ "homepage": "https://github.com/mafintosh/generate-object-property",
+ "main": "index.js",
+ "author": {
+ "name": "Mathias Buus",
+ "url": "@mafintosh"
+ },
+ "license": "MIT",
+ "gitHead": "0dd7d411018de54b2eae63b424c15b3e50bd678c",
+ "_id": "generate-object-property@1.2.0",
+ "_shasum": "9c0e1c40308ce804f4783618b937fa88f99d50d0",
+ "_from": "generate-object-property@>=1.1.0 <2.0.0",
+ "_npmVersion": "2.9.0",
+ "_nodeVersion": "2.0.1",
+ "_npmUser": {
+ "name": "mafintosh",
+ "email": "mathiasbuus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mafintosh",
+ "email": "mathiasbuus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "9c0e1c40308ce804f4783618b937fa88f99d50d0",
+ "tarball": "http://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz"
+}
--- /dev/null
+var tape = require('tape')
+var gen = require('./')
+
+tape('valid', function(t) {
+ t.same(gen('a', 'b'), 'a.b')
+ t.end()
+})
+
+tape('invalid', function(t) {
+ t.same(gen('a', '-b'), 'a["-b"]')
+ t.end()
+})
\ No newline at end of file
--- /dev/null
+language: "node_js"
+node_js:
+ - 0.4
+ - 0.5
+ - 0.6
+ - 0.8
--- /dev/null
+# JSON Pointer for nodejs
+
+This is an implementation of [JSON Pointer](http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-08).
+
+## Usage
+
+ var jsonpointer = require("jsonpointer");
+ var obj = { foo: 1, bar: { baz: 2}, qux: [3, 4, 5]};
+ var one = jsonpointer.get(obj, "/foo");
+ var two = jsonpointer.get(obj, "/bar/baz");
+ var three = jsonpointer.get(obj, "/qux/0");
+ var four = jsonpointer.get(obj, "/qux/1");
+ var five = jsonpointer.get(obj, "/qux/2");
+
+ jsonpointer.set(obj, "/foo", 6); // obj.foo = 6;
+
+## Testing
+
+ $ node test.js
+ All tests pass.
+ $
+
+[](undefined)
+
+## Author
+
+(c) 2011 Jan Lehnardt <jan@apache.org>
+
+## License
+
+MIT License.
\ No newline at end of file
--- /dev/null
+var console = require("console");
+
+var untilde = function(str) {
+ return str.replace(/~./g, function(m) {
+ switch (m) {
+ case "~0":
+ return "~";
+ case "~1":
+ return "/";
+ }
+ throw("Invalid tilde escape: " + m);
+ });
+}
+
+var traverse = function(obj, pointer, value) {
+ // assert(isArray(pointer))
+ var part = untilde(pointer.shift());
+ if(typeof obj[part] === "undefined") {
+ throw("Value for pointer '" + pointer + "' not found.");
+ return;
+ }
+ if(pointer.length !== 0) { // keep traversin!
+ return traverse(obj[part], pointer, value);
+ }
+ // we're done
+ if(typeof value === "undefined") {
+ // just reading
+ return obj[part];
+ }
+ // set new value, return old value
+ var old_value = obj[part];
+ if(value === null) {
+ delete obj[part];
+ } else {
+ obj[part] = value;
+ }
+ return old_value;
+}
+
+var validate_input = function(obj, pointer) {
+ if(typeof obj !== "object") {
+ throw("Invalid input object.");
+ }
+
+ if(pointer === "") {
+ return [];
+ }
+
+ if(!pointer) {
+ throw("Invalid JSON pointer.");
+ }
+
+ pointer = pointer.split("/");
+ var first = pointer.shift();
+ if (first !== "") {
+ throw("Invalid JSON pointer.");
+ }
+
+ return pointer;
+}
+
+var get = function(obj, pointer) {
+ pointer = validate_input(obj, pointer);
+ if (pointer.length === 0) {
+ return obj;
+ }
+ return traverse(obj, pointer);
+}
+
+var set = function(obj, pointer, value) {
+ pointer = validate_input(obj, pointer);
+ if (pointer.length === 0) {
+ throw("Invalid JSON pointer for set.")
+ }
+ return traverse(obj, pointer, value);
+}
+
+exports.get = get
+exports.set = set
--- /dev/null
+{
+ "name": "jsonpointer",
+ "description": "Simple JSON Addressing.",
+ "tags": [
+ "util",
+ "simple",
+ "util",
+ "utility"
+ ],
+ "version": "1.1.0",
+ "author": {
+ "name": "Jan Lehnardt",
+ "email": "jan@apache.org"
+ },
+ "contributors": [
+ {
+ "name": "Joe Hildebrand",
+ "email": "joe-github@cursive.net"
+ },
+ {
+ "name": "Filip Noetzel"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/janl/node-jsonpointer.git"
+ },
+ "bugs": {
+ "url": "http://github.com/janl/node-jsonpointer/issues"
+ },
+ "engines": [
+ "node >= 0.4.9"
+ ],
+ "main": "./jsonpointer",
+ "scripts": {
+ "test": "node test.js"
+ },
+ "readme": "# JSON Pointer for nodejs\n\nThis is an implementation of [JSON Pointer](http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-08).\n\n## Usage\n\n var jsonpointer = require(\"jsonpointer\");\n var obj = { foo: 1, bar: { baz: 2}, qux: [3, 4, 5]};\n var one = jsonpointer.get(obj, \"/foo\");\n var two = jsonpointer.get(obj, \"/bar/baz\");\n var three = jsonpointer.get(obj, \"/qux/0\");\n var four = jsonpointer.get(obj, \"/qux/1\");\n var five = jsonpointer.get(obj, \"/qux/2\");\n\n jsonpointer.set(obj, \"/foo\", 6); // obj.foo = 6;\n\n## Testing\n\n $ node test.js\n All tests pass.\n $\n\n[](undefined)\n\n## Author\n\n(c) 2011 Jan Lehnardt <jan@apache.org>\n\n## License\n\nMIT License.",
+ "readmeFilename": "README.md",
+ "_id": "jsonpointer@1.1.0",
+ "dist": {
+ "shasum": "c3c72efaed3b97154163dc01dd349e1cfe0f80fc",
+ "tarball": "http://registry.npmjs.org/jsonpointer/-/jsonpointer-1.1.0.tgz"
+ },
+ "_npmVersion": "1.1.69",
+ "_npmUser": {
+ "name": "jan",
+ "email": "jan@apache.org"
+ },
+ "maintainers": [
+ {
+ "name": "jan",
+ "email": "jan@apache.org"
+ }
+ ],
+ "directories": {},
+ "_shasum": "c3c72efaed3b97154163dc01dd349e1cfe0f80fc",
+ "_resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-1.1.0.tgz",
+ "_from": "jsonpointer@>=1.1.0 <2.0.0"
+}
--- /dev/null
+var assert = require("assert");
+var console = require("console");
+var jsonpointer = require("./jsonpointer");
+
+var obj = {
+ a: 1,
+ b: {
+ c: 2
+ },
+ d: {
+ e: [{a:3}, {b:4}, {c:5}]
+ }
+};
+
+assert.equal(jsonpointer.get(obj, "/a"), 1);
+assert.equal(jsonpointer.get(obj, "/b/c"), 2);
+assert.equal(jsonpointer.get(obj, "/d/e/0/a"), 3);
+assert.equal(jsonpointer.get(obj, "/d/e/1/b"), 4);
+assert.equal(jsonpointer.get(obj, "/d/e/2/c"), 5);
+
+// set returns old value
+assert.equal(jsonpointer.set(obj, "/a", 2), 1);
+assert.equal(jsonpointer.set(obj, "/b/c", 3), 2);
+assert.equal(jsonpointer.set(obj, "/d/e/0/a", 4), 3);
+assert.equal(jsonpointer.set(obj, "/d/e/1/b", 5), 4);
+assert.equal(jsonpointer.set(obj, "/d/e/2/c", 6), 5);
+
+assert.equal(jsonpointer.get(obj, "/a"), 2);
+assert.equal(jsonpointer.get(obj, "/b/c"), 3);
+assert.equal(jsonpointer.get(obj, "/d/e/0/a"), 4);
+assert.equal(jsonpointer.get(obj, "/d/e/1/b"), 5);
+assert.equal(jsonpointer.get(obj, "/d/e/2/c"), 6);
+
+assert.equal(jsonpointer.get(obj, ""), obj);
+assert.throws(function() {
+ assert.equal(jsonpointer.get(obj, "a"), 3);
+});
+
+var complexKeys = {
+ "a/b": {
+ c: 1
+ },
+ d: {
+ "e/f": 2
+ },
+ "~1": 3,
+ "01": 4
+}
+
+assert.equal(jsonpointer.get(complexKeys, "/a~1b/c"), 1);
+assert.equal(jsonpointer.get(complexKeys, "/d/e~1f"), 2);
+assert.equal(jsonpointer.get(complexKeys, "/~01"), 3);
+assert.equal(jsonpointer.get(complexKeys, "/01"), 4);
+assert.throws(function() {
+ assert.equal(jsonpointer.get(complexKeys, "/a/b/c"), 1);
+});
+assert.throws(function() {
+ assert.equal(jsonpointer.get(complexKeys, "/~1"), 3);
+});
+
+// draft-ietf-appsawg-json-pointer-08 has special array rules
+var ary = [ "zero", "one", "two" ];
+
+assert.throws(function() {
+ assert.equal(jsonpointer.get(ary, "/01"), "one");
+});
+//assert.equal(jsonpointer.set(ary, "/-", "three"), null);
+//assert.equal(ary[3], "three");
+
+// Examples from the draft:
+var example = {
+ "foo": ["bar", "baz"],
+ "": 0,
+ "a/b": 1,
+ "c%d": 2,
+ "e^f": 3,
+ "g|h": 4,
+ "i\\j": 5,
+ "k\"l": 6,
+ " ": 7,
+ "m~n": 8
+};
+
+assert.equal(jsonpointer.get(example, ""), example);
+var ans = jsonpointer.get(example, "/foo");
+assert.equal(ans.length, 2);
+assert.equal(ans[0], "bar");
+assert.equal(ans[1], "baz");
+assert.equal(jsonpointer.get(example, "/foo/0"), "bar");
+assert.equal(jsonpointer.get(example, "/"), 0);
+assert.equal(jsonpointer.get(example, "/a~1b"), 1);
+assert.equal(jsonpointer.get(example, "/c%d"), 2);
+assert.equal(jsonpointer.get(example, "/e^f"), 3);
+assert.equal(jsonpointer.get(example, "/g|h"), 4);
+assert.equal(jsonpointer.get(example, "/i\\j"), 5);
+assert.equal(jsonpointer.get(example, "/k\"l"), 6);
+assert.equal(jsonpointer.get(example, "/ "), 7);
+assert.equal(jsonpointer.get(example, "/m~0n"), 8);
+
+console.log("All tests pass.");
--- /dev/null
+{
+ "maxdepth": 4,
+ "maxstatements": 200,
+ "maxcomplexity": 12,
+ "maxlen": 80,
+ "maxparams": 5,
+
+ "curly": true,
+ "eqeqeq": true,
+ "immed": true,
+ "latedef": false,
+ "noarg": true,
+ "noempty": true,
+ "nonew": true,
+ "undef": true,
+ "unused": "vars",
+ "trailing": true,
+
+ "quotmark": true,
+ "expr": true,
+ "asi": true,
+
+ "browser": false,
+ "esnext": true,
+ "devel": false,
+ "node": false,
+ "nonstandard": false,
+
+ "predef": ["require", "module", "__dirname", "__filename"]
+}
--- /dev/null
+node_modules
--- /dev/null
+Copyright (c) 2012-2014 Raynos.
+
+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.
--- /dev/null
+browser:
+ node ./support/compile
+
+.PHONY: browser
\ No newline at end of file
--- /dev/null
+# xtend
+
+[![browser support][3]][4]
+
+[](http://github.com/badges/stability-badges)
+
+Extend like a boss
+
+xtend is a basic utility library which allows you to extend an object by appending all of the properties from each object in a list. When there are identical properties, the right-most property takes precedence.
+
+## Examples
+
+```js
+var extend = require("xtend")
+
+// extend returns a new object. Does not mutate arguments
+var combination = extend({
+ a: "a",
+ b: 'c'
+}, {
+ b: "b"
+})
+// { a: "a", b: "b" }
+```
+
+## Stability status: Locked
+
+## MIT Licenced
+
+
+ [3]: http://ci.testling.com/Raynos/xtend.png
+ [4]: http://ci.testling.com/Raynos/xtend
--- /dev/null
+module.exports = extend
+
+function extend() {
+ var target = {}
+
+ for (var i = 0; i < arguments.length; i++) {
+ var source = arguments[i]
+
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ target[key] = source[key]
+ }
+ }
+ }
+
+ return target
+}
--- /dev/null
+module.exports = extend
+
+function extend(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i]
+
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ target[key] = source[key]
+ }
+ }
+ }
+
+ return target
+}
--- /dev/null
+{
+ "name": "xtend",
+ "version": "4.0.0",
+ "description": "extend like a boss",
+ "keywords": [
+ "extend",
+ "merge",
+ "options",
+ "opts",
+ "object",
+ "array"
+ ],
+ "author": {
+ "name": "Raynos",
+ "email": "raynos2@gmail.com"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/Raynos/xtend.git"
+ },
+ "main": "immutable",
+ "scripts": {
+ "test": "node test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tape": "~1.1.0"
+ },
+ "homepage": "https://github.com/Raynos/xtend",
+ "contributors": [
+ {
+ "name": "Jake Verbaten"
+ },
+ {
+ "name": "Matt Esch"
+ }
+ ],
+ "bugs": {
+ "url": "https://github.com/Raynos/xtend/issues",
+ "email": "raynos2@gmail.com"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://github.com/raynos/xtend/raw/master/LICENSE"
+ }
+ ],
+ "testling": {
+ "files": "test.js",
+ "browsers": [
+ "ie/7..latest",
+ "firefox/16..latest",
+ "firefox/nightly",
+ "chrome/22..latest",
+ "chrome/canary",
+ "opera/12..latest",
+ "opera/next",
+ "safari/5.1..latest",
+ "ipad/6.0..latest",
+ "iphone/6.0..latest"
+ ]
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "gitHead": "94a95d76154103290533b2c55ffa0fe4be16bfef",
+ "_id": "xtend@4.0.0",
+ "_shasum": "8bc36ff87aedbe7ce9eaf0bca36b2354a743840f",
+ "_from": "xtend@>=4.0.0 <5.0.0",
+ "_npmVersion": "1.4.15",
+ "_npmUser": {
+ "name": "raynos",
+ "email": "raynos2@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "raynos",
+ "email": "raynos2@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "8bc36ff87aedbe7ce9eaf0bca36b2354a743840f",
+ "tarball": "http://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+var test = require("tape")
+var extend = require("./")
+var mutableExtend = require("./mutable")
+
+test("merge", function(assert) {
+ var a = { a: "foo" }
+ var b = { b: "bar" }
+
+ assert.deepEqual(extend(a, b), { a: "foo", b: "bar" })
+ assert.end()
+})
+
+test("replace", function(assert) {
+ var a = { a: "foo" }
+ var b = { a: "bar" }
+
+ assert.deepEqual(extend(a, b), { a: "bar" })
+ assert.end()
+})
+
+test("undefined", function(assert) {
+ var a = { a: undefined }
+ var b = { b: "foo" }
+
+ assert.deepEqual(extend(a, b), { a: undefined, b: "foo" })
+ assert.deepEqual(extend(b, a), { a: undefined, b: "foo" })
+ assert.end()
+})
+
+test("handle 0", function(assert) {
+ var a = { a: "default" }
+ var b = { a: 0 }
+
+ assert.deepEqual(extend(a, b), { a: 0 })
+ assert.deepEqual(extend(b, a), { a: "default" })
+ assert.end()
+})
+
+test("is immutable", function (assert) {
+ var record = {}
+
+ extend(record, { foo: "bar" })
+ assert.equal(record.foo, undefined)
+ assert.end()
+})
+
+test("null as argument", function (assert) {
+ var a = { foo: "bar" }
+ var b = null
+ var c = void 0
+
+ assert.deepEqual(extend(b, a, c), { foo: "bar" })
+ assert.end()
+})
+
+test("mutable", function (assert) {
+ var a = { foo: "bar" }
+
+ mutableExtend(a, { bar: "baz" })
+
+ assert.equal(a.bar, "baz")
+ assert.end()
+})
--- /dev/null
+{
+ "name": "is-my-json-valid",
+ "version": "2.12.0",
+ "description": "A JSONSchema validator that uses code generation to be extremely fast",
+ "main": "index.js",
+ "dependencies": {
+ "generate-function": "^2.0.0",
+ "generate-object-property": "^1.1.0",
+ "jsonpointer": "^1.1.0",
+ "xtend": "^4.0.0"
+ },
+ "devDependencies": {
+ "tape": "^2.13.4"
+ },
+ "scripts": {
+ "test": "tape test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mafintosh/is-my-json-valid"
+ },
+ "keywords": [
+ "json",
+ "schema",
+ "orderly",
+ "jsonschema"
+ ],
+ "author": {
+ "name": "Mathias Buus"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/mafintosh/is-my-json-valid/issues"
+ },
+ "homepage": "https://github.com/mafintosh/is-my-json-valid",
+ "gitHead": "fc2d995ecb1bdb0f367ddf114dfd069265043c6c",
+ "_id": "is-my-json-valid@2.12.0",
+ "_shasum": "8fa6c408b26be95b45a23e8f8c4b464a53874d2b",
+ "_from": "is-my-json-valid@>=2.10.1 <3.0.0",
+ "_npmVersion": "2.9.0",
+ "_nodeVersion": "2.0.1",
+ "_npmUser": {
+ "name": "mafintosh",
+ "email": "mathiasbuus@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mafintosh",
+ "email": "mathiasbuus@gmail.com"
+ },
+ {
+ "name": "watson",
+ "email": "w@tson.dk"
+ },
+ {
+ "name": "freeall",
+ "email": "freeall@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "8fa6c408b26be95b45a23e8f8c4b464a53874d2b",
+ "tarball": "http://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.0.tgz"
+}
--- /dev/null
+var fs = require('fs')
+var path = require('path')
+var compile = require('./')
+
+delete require.cache[require.resolve(__filename)]
+
+module.exports = function(file, opts) {
+ file = path.join(path.dirname(module.parent.filename), file)
+ if (!fs.existsSync(file) && fs.existsSync(file+'.schema')) file += '.schema'
+ if (!fs.existsSync(file) && fs.existsSync(file+'.json')) file += '.json'
+ return compile(fs.readFileSync(file, 'utf-8'), opts)
+}
--- /dev/null
+exports.valid = {
+ fullName : "John Doe",
+ age : 47,
+ state : "Massachusetts",
+ city : "Boston",
+ zip : 16417,
+ married : false,
+ dozen : 12,
+ dozenOrBakersDozen : 13,
+ favoriteEvenNumber : 14,
+ topThreeFavoriteColors : [ "red", "blue", "green" ],
+ favoriteSingleDigitWholeNumbers : [ 7 ],
+ favoriteFiveLetterWord : "coder",
+ emailAddresses :
+ [
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@letters-in-local.org",
+ "01234567890@numbers-in-local.net",
+ "&'*+-./=?^_{}~@other-valid-characters-in-local.net",
+ "mixed-1234-in-{+^}-local@sld.net",
+ "a@single-character-in-local.org",
+ "\"quoted\"@sld.com",
+ "\"\\e\\s\\c\\a\\p\\e\\d\"@sld.com",
+ "\"quoted-at-sign@sld.org\"@sld.com",
+ "\"escaped\\\"quote\"@sld.com",
+ "\"back\\slash\"@sld.com",
+ "one-character-third-level@a.example.com",
+ "single-character-in-sld@x.org",
+ "local@dash-in-sld.com",
+ "letters-in-sld@123.com",
+ "one-letter-sld@x.org",
+ "uncommon-tld@sld.museum",
+ "uncommon-tld@sld.travel",
+ "uncommon-tld@sld.mobi",
+ "country-code-tld@sld.uk",
+ "country-code-tld@sld.rw",
+ "local@sld.newTLD",
+ "the-total-length@of-an-entire-address.cannot-be-longer-than-two-hundred-and-fifty-four-characters.and-this-address-is-254-characters-exactly.so-it-should-be-valid.and-im-going-to-add-some-more-words-here.to-increase-the-lenght-blah-blah-blah-blah-bla.org",
+ "the-character-limit@for-each-part.of-the-domain.is-sixty-three-characters.this-is-exactly-sixty-three-characters-so-it-is-valid-blah-blah.com",
+ "local@sub.domains.com"
+ ],
+ ipAddresses : [ "127.0.0.1", "24.48.64.2", "192.168.1.1", "209.68.44.3", "2.2.2.2" ]
+}
+
+exports.invalid = {
+ fullName : null,
+ age : -1,
+ state : 47,
+ city : false,
+ zip : [null],
+ married : "yes",
+ dozen : 50,
+ dozenOrBakersDozen : "over 9000",
+ favoriteEvenNumber : 15,
+ topThreeFavoriteColors : [ "red", 5 ],
+ favoriteSingleDigitWholeNumbers : [ 78, 2, 999 ],
+ favoriteFiveLetterWord : "codernaut",
+ emailAddresses : [],
+ ipAddresses : [ "999.0.099.1", "294.48.64.2346", false, "2221409.64214128.42414.235233", "124124.12412412" ]
+}
+
+exports.schema = { // from cosmic thingy
+ name : "test",
+ type : "object",
+ additionalProperties : false,
+ required : ["fullName", "age", "zip", "married", "dozen", "dozenOrBakersDozen", "favoriteEvenNumber", "topThreeFavoriteColors", "favoriteSingleDigitWholeNumbers", "favoriteFiveLetterWord", "emailAddresses", "ipAddresses"],
+ properties :
+ {
+ fullName : { type : "string" },
+ age : { type : "integer", minimum : 0 },
+ optionalItem : { type : "string" },
+ state : { type : "string" },
+ city : { type : "string" },
+ zip : { type : "integer", minimum : 0, maximum : 99999 },
+ married : { type : "boolean" },
+ dozen : { type : "integer", minimum : 12, maximum : 12 },
+ dozenOrBakersDozen : { type : "integer", minimum : 12, maximum : 13 },
+ favoriteEvenNumber : { type : "integer", multipleOf : 2 },
+ topThreeFavoriteColors : { type : "array", minItems : 3, maxItems : 3, uniqueItems : true, items : { type : "string" }},
+ favoriteSingleDigitWholeNumbers : { type : "array", minItems : 1, maxItems : 10, uniqueItems : true, items : { type : "integer", minimum : 0, maximum : 9 }},
+ favoriteFiveLetterWord : { type : "string", minLength : 5, maxLength : 5 },
+ emailAddresses : { type : "array", minItems : 1, uniqueItems : true, items : { type : "string", format : "email" }},
+ ipAddresses : { type : "array", uniqueItems : true, items : { type : "string", format : "ipv4" }},
+ }
+ }
\ No newline at end of file
--- /dev/null
+[
+ {
+ "description": "additionalItems as schema",
+ "schema": {
+ "items": [{}],
+ "additionalItems": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "additional items match schema",
+ "data": [ null, 2, 3, 4 ],
+ "valid": true
+ },
+ {
+ "description": "additional items do not match schema",
+ "data": [ null, 2, 3, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items is schema, no additionalItems",
+ "schema": {
+ "items": {},
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "all items match schema",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "array of items with no additionalItems",
+ "schema": {
+ "items": [{}, {}, {}],
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "no additional items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalItems as false without items",
+ "schema": {"additionalItems": false},
+ "tests": [
+ {
+ "description":
+ "items defaults to empty schema so everything is valid",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems are allowed by default",
+ "schema": {"items": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores non-objects",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "integer",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "integer",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": {"maximum": 18446744073709551615},
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "maximum": 972783798187987123879878123.18878137,
+ "exclusiveMaximum": true
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": {"minimum": -18446744073709551615},
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "minimum": -972783798187987123879878123.18878137,
+ "exclusiveMinimum": true
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "valid definition",
+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": "integer"}
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid definition",
+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+ "tests": [
+ {
+ "description": "invalid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": 1}
+ }
+ },
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "dependencies",
+ "schema": {
+ "dependencies": {"bar": ["foo"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores non-objects",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies",
+ "schema": {
+ "dependencies": {"quux": ["foo", "bar"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies subschema",
+ "schema": {
+ "dependencies": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": {"format": "date-time"},
+ "tests": [
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validation of URIs",
+ "schema": {"format": "uri"},
+ "tests": [
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validation of e-mail addresses",
+ "schema": {"format": "email"},
+ "tests": [
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validation of IP addresses",
+ "schema": {"format": "ipv4"},
+ "tests": [
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": {"format": "ipv6"},
+ "tests": [
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validation of host names",
+ "schema": {"format": "hostname"},
+ "tests": [
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "an array of schemas for items",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores non-objects",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "maximum": 3.0,
+ "exclusiveMaximum": true
+ },
+ "tests": [
+ {
+ "description": "below the maximum is still valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores non-objects",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "minimum": 1.1,
+ "exclusiveMinimum": true
+ },
+ "tests": [
+ {
+ "description": "above the minimum is still valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ }
+
+]
--- /dev/null
+[
+ {
+ "description": "validation of null and format",
+ "schema": {"type": ["null", "string"], "format": "date-time"},
+ "tests": [
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "allow null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": true,
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores non-objects",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"$ref": "#/items/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "tilda~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"},
+ "properties": {
+ "tilda": {"$ref": "#/tilda~0field"},
+ "slash": {"$ref": "#/slash~1field"},
+ "percent": {"$ref": "#/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilda",
+ "data": {"tilda": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "definitions": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/definitions/a"},
+ "c": {"$ref": "#/definitions/b"}
+ },
+ "$ref": "#/definitions/c"
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "change resolution scope",
+ "schema": {
+ "id": "http://localhost:1234/",
+ "items": {
+ "id": "folder/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "changed scope ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "changed scope ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ }
+ ]
+ }
+]
--- /dev/null
+var tape = require('tape')
+var fs = require('fs')
+var validator = require('../')
+
+var files = fs.readdirSync(__dirname+'/json-schema-draft4')
+ .map(function(file) {
+ if (file === 'definitions.json') return null
+ if (file === 'refRemote.json') return null
+ return require('./json-schema-draft4/'+file)
+ })
+ .filter(Boolean)
+
+files.forEach(function(file) {
+ file.forEach(function(f) {
+ tape('json-schema-test-suite '+f.description, function(t) {
+ var validate = validator(f.schema)
+ f.tests.forEach(function(test) {
+ t.same(validate(test.data), test.valid, test.description)
+ })
+ t.end()
+ })
+ })
+})
--- /dev/null
+var tape = require('tape')
+var cosmic = require('./fixtures/cosmic')
+var validator = require('../')
+var validatorRequire = require('../require')
+
+tape('simple', function(t) {
+ var schema = {
+ required: true,
+ type: 'object',
+ properties: {
+ hello: {type:'string', required:true}
+ }
+ }
+
+ var validate = validator(schema)
+
+ t.ok(validate({hello: 'world'}), 'should be valid')
+ t.notOk(validate(), 'should be invalid')
+ t.notOk(validate({}), 'should be invalid')
+ t.end()
+})
+
+tape('advanced', function(t) {
+ var validate = validator(cosmic.schema)
+
+ t.ok(validate(cosmic.valid), 'should be valid')
+ t.notOk(validate(cosmic.invalid), 'should be invalid')
+ t.end()
+})
+
+tape('greedy/false', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ x: {
+ type: 'number'
+ }
+ },
+ required: ['x', 'y']
+ });
+ t.notOk(validate({}), 'should be invalid')
+ t.strictEqual(validate.errors.length, 2);
+ t.strictEqual(validate.errors[0].field, 'data.x')
+ t.strictEqual(validate.errors[0].message, 'is required')
+ t.strictEqual(validate.errors[1].field, 'data.y')
+ t.strictEqual(validate.errors[1].message, 'is required')
+ t.notOk(validate({x: 'string'}), 'should be invalid')
+ t.strictEqual(validate.errors.length, 1);
+ t.strictEqual(validate.errors[0].field, 'data.y')
+ t.strictEqual(validate.errors[0].message, 'is required')
+ t.notOk(validate({x: 'string', y: 'value'}), 'should be invalid')
+ t.strictEqual(validate.errors.length, 1);
+ t.strictEqual(validate.errors[0].field, 'data.x')
+ t.strictEqual(validate.errors[0].message, 'is the wrong type')
+ t.end();
+});
+
+tape('greedy/true', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ x: {
+ type: 'number'
+ }
+ },
+ required: ['x', 'y']
+ }, {
+ greedy: true
+ });
+ t.notOk(validate({}), 'should be invalid')
+ t.strictEqual(validate.errors.length, 2);
+ t.strictEqual(validate.errors[0].field, 'data.x')
+ t.strictEqual(validate.errors[0].message, 'is required')
+ t.strictEqual(validate.errors[1].field, 'data.y')
+ t.strictEqual(validate.errors[1].message, 'is required')
+ t.notOk(validate({x: 'string'}), 'should be invalid')
+ t.strictEqual(validate.errors.length, 2);
+ t.strictEqual(validate.errors[0].field, 'data.y')
+ t.strictEqual(validate.errors[0].message, 'is required')
+ t.strictEqual(validate.errors[1].field, 'data.x')
+ t.strictEqual(validate.errors[1].message, 'is the wrong type')
+ t.notOk(validate({x: 'string', y: 'value'}), 'should be invalid')
+ t.strictEqual(validate.errors.length, 1);
+ t.strictEqual(validate.errors[0].field, 'data.x')
+ t.strictEqual(validate.errors[0].message, 'is the wrong type')
+ t.ok(validate({x: 1, y: 'value'}), 'should be invalid')
+ t.end();
+});
+
+tape('additional props', function(t) {
+ var validate = validator({
+ type: 'object',
+ additionalProperties: false
+ }, {
+ verbose: true
+ })
+
+ t.ok(validate({}))
+ t.notOk(validate({foo:'bar'}))
+ t.ok(validate.errors[0].value === 'data.foo', 'should output the property not allowed in verbose mode')
+ t.end()
+})
+
+tape('array', function(t) {
+ var validate = validator({
+ type: 'array',
+ required: true,
+ items: {
+ type: 'string'
+ }
+ })
+
+ t.notOk(validate({}), 'wrong type')
+ t.notOk(validate(), 'is required')
+ t.ok(validate(['test']))
+ t.end()
+})
+
+tape('nested array', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ list: {
+ type: 'array',
+ required: true,
+ items: {
+ type: 'string'
+ }
+ }
+ }
+ })
+
+ t.notOk(validate({}), 'is required')
+ t.ok(validate({list:['test']}))
+ t.notOk(validate({list:[1]}))
+ t.end()
+})
+
+tape('enum', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ foo: {
+ type: 'number',
+ required: true,
+ enum: [42]
+ }
+ }
+ })
+
+ t.notOk(validate({}), 'is required')
+ t.ok(validate({foo:42}))
+ t.notOk(validate({foo:43}))
+ t.end()
+})
+
+tape('minimum/maximum', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ foo: {
+ type: 'number',
+ minimum: 0,
+ maximum: 0
+ }
+ }
+ })
+
+ t.notOk(validate({foo:-42}))
+ t.ok(validate({foo:0}))
+ t.notOk(validate({foo:42}))
+ t.end()
+})
+
+tape('exclusiveMinimum/exclusiveMaximum', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ foo: {
+ type: 'number',
+ minimum: 10,
+ maximum: 20,
+ exclusiveMinimum: true,
+ exclusiveMaximum: true
+ }
+ }
+ })
+
+ t.notOk(validate({foo:10}))
+ t.ok(validate({foo:11}))
+ t.notOk(validate({foo:20}))
+ t.ok(validate({foo:19}))
+ t.end()
+})
+
+tape('custom format', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ foo: {
+ type: 'string',
+ format: 'as'
+ }
+ }
+ }, {formats: {as:/^a+$/}})
+
+ t.notOk(validate({foo:''}), 'not as')
+ t.notOk(validate({foo:'b'}), 'not as')
+ t.notOk(validate({foo:'aaab'}), 'not as')
+ t.ok(validate({foo:'a'}), 'as')
+ t.ok(validate({foo:'aaaaaa'}), 'as')
+ t.end()
+})
+
+tape('custom format function', function(t) {
+ var validate = validator({
+ type: 'object',
+ properties: {
+ foo: {
+ type: 'string',
+ format: 'as'
+ }
+ }
+ }, {formats: {as:function(s) { return /^a+$/.test(s) } }})
+
+ t.notOk(validate({foo:''}), 'not as')
+ t.notOk(validate({foo:'b'}), 'not as')
+ t.notOk(validate({foo:'aaab'}), 'not as')
+ t.ok(validate({foo:'a'}), 'as')
+ t.ok(validate({foo:'aaaaaa'}), 'as')
+ t.end()
+})
+
+tape('do not mutate schema', function(t) {
+ var sch = {
+ items: [
+ {}
+ ],
+ additionalItems: {
+ type: 'integer'
+ }
+ }
+
+ var copy = JSON.parse(JSON.stringify(sch))
+
+ validator(sch)
+
+ t.same(sch, copy, 'did not mutate')
+ t.end()
+})
+
+tape('#toJSON()', function(t) {
+ var schema = {
+ required: true,
+ type: 'object',
+ properties: {
+ hello: {type:'string', required:true}
+ }
+ }
+
+ var validate = validator(schema)
+
+ t.deepEqual(validate.toJSON(), schema, 'should return original schema')
+ t.end()
+})
+
+tape('external schemas', function(t) {
+ var ext = {type: 'string'}
+ var schema = {
+ required: true,
+ $ref: '#ext'
+ }
+
+ var validate = validator(schema, {schemas: {ext:ext}})
+
+ t.ok(validate('hello string'), 'is a string')
+ t.notOk(validate(42), 'not a string')
+ t.end()
+})
+
+tape('top-level external schema', function(t) {
+ var defs = {
+ "string": {
+ type: "string"
+ },
+ "sex": {
+ type: "string",
+ enum: ["male", "female", "other"]
+ }
+ }
+ var schema = {
+ type: "object",
+ properties: {
+ "name": { $ref: "definitions.json#/string" },
+ "sex": { $ref: "definitions.json#/sex" }
+ },
+ required: ["name", "sex"]
+ }
+
+ var validate = validator(schema, {
+ schemas: {
+ "definitions.json": defs
+ }
+ })
+ t.ok(validate({name:"alice", sex:"female"}), 'is an object')
+ t.notOk(validate({name:"alice", sex: "bob"}), 'recognizes external schema')
+ t.notOk(validate({name:2, sex: "female"}), 'recognizes external schema')
+ t.end()
+})
+
+tape('nested required array decl', function(t) {
+ var schema = {
+ properties: {
+ x: {
+ type: 'object',
+ properties: {
+ y: {
+ type: 'object',
+ properties: {
+ z: {
+ type: 'string'
+ }
+ },
+ required: ['z']
+ }
+ }
+ }
+ },
+ required: ['x']
+ }
+
+ var validate = validator(schema)
+
+ t.ok(validate({x: {}}), 'should be valid')
+ t.notOk(validate({}), 'should not be valid')
+ t.strictEqual(validate.errors[0].field, 'data.x', 'should output the missing field')
+ t.end()
+})
+
+tape('verbose mode', function(t) {
+ var schema = {
+ required: true,
+ type: 'object',
+ properties: {
+ hello: {
+ required: true,
+ type: 'string'
+ }
+ }
+ };
+
+ var validate = validator(schema, {verbose: true})
+
+ t.ok(validate({hello: 'string'}), 'should be valid')
+ t.notOk(validate({hello: 100}), 'should not be valid')
+ t.strictEqual(validate.errors[0].value, 100, 'error object should contain the invalid value')
+ t.end()
+})
+
+tape('additional props in verbose mode', function(t) {
+ var schema = {
+ type: 'object',
+ required: true,
+ additionalProperties: false,
+ properties: {
+ foo: {
+ type: 'string'
+ },
+ 'hello world': {
+ type: 'object',
+ required: true,
+ additionalProperties: false,
+ properties: {
+ foo: {
+ type: 'string'
+ }
+ }
+ }
+ }
+ };
+
+ var validate = validator(schema, {verbose: true})
+
+ validate({'hello world': {bar: 'string'}});
+
+ t.strictEqual(validate.errors[0].value, 'data["hello world"].bar', 'should output the path to the additional prop in the error')
+ t.end()
+})
+
+tape('Date.now() is an integer', function(t) {
+ var schema = {type: 'integer'}
+ var validate = validator(schema)
+
+ t.ok(validate(Date.now()), 'is integer')
+ t.end()
+})
+
+tape('field shows item index in arrays', function(t) {
+ var schema = {
+ type: 'array',
+ items: {
+ type: 'array',
+ items: {
+ properties: {
+ foo: {
+ type: 'string',
+ required: true
+ }
+ }
+ }
+ }
+ }
+
+ var validate = validator(schema)
+
+ validate([
+ [
+ { foo: 'test' },
+ { foo: 'test' }
+ ],
+ [
+ { foo: 'test' },
+ { baz: 'test' }
+ ]
+ ])
+
+ t.strictEqual(validate.errors[0].field, 'data.1.1.foo', 'should output the field with specific index of failing item in the error')
+ t.end()
+})
--- /dev/null
+{
+ "version": "1.7.0",
+ "name": "har-validator",
+ "description": "Extremely fast HTTP Archive (HAR) validator using JSON Schema",
+ "author": {
+ "name": "Ahmad Nassri",
+ "email": "ahmad@ahmadnassri.com",
+ "url": "https://www.ahmadnassri.com/"
+ },
+ "homepage": "https://github.com/ahmadnassri/har-validator",
+ "license": "MIT",
+ "main": "./src/index.js",
+ "bin": {
+ "har-validator": "./bin/har-validator"
+ },
+ "keywords": [
+ "har",
+ "http",
+ "archive",
+ "validate",
+ "validator"
+ ],
+ "engines": {
+ "node": ">=0.10"
+ },
+ "files": [
+ "bin",
+ "src"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ahmadnassri/har-validator"
+ },
+ "bugs": {
+ "url": "https://github.com/ahmadnassri/har-validator/issues"
+ },
+ "scripts": {
+ "test": "standard && mocha --reporter spec",
+ "coverage": "istanbul cover ./node_modules/mocha/bin/_mocha",
+ "codeclimate": "codeclimate < coverage/lcov.info"
+ },
+ "devDependencies": {
+ "codeclimate-test-reporter": "0.0.4",
+ "istanbul": "^0.3.13",
+ "mocha": "^2.2.4",
+ "require-directory": "^2.1.0",
+ "should": "^6.0.1",
+ "standard": "^3.7.2"
+ },
+ "dependencies": {
+ "bluebird": "^2.9.25",
+ "chalk": "^1.0.0",
+ "commander": "^2.8.1",
+ "is-my-json-valid": "^2.10.1"
+ },
+ "gitHead": "74aab6b4994681cdb961db88f5129c5096eaf06a",
+ "_id": "har-validator@1.7.0",
+ "_shasum": "563f8c58edca6410e2e408b0e540161da580dc46",
+ "_from": "har-validator@>=1.4.0 <2.0.0",
+ "_npmVersion": "2.5.1",
+ "_nodeVersion": "0.12.1",
+ "_npmUser": {
+ "name": "ahmadnassri",
+ "email": "ahmad@ahmadnassri.com"
+ },
+ "maintainers": [
+ {
+ "name": "ahmadnassri",
+ "email": "ahmad@ahmadnassri.com"
+ }
+ ],
+ "dist": {
+ "shasum": "563f8c58edca6410e2e408b0e540161da580dc46",
+ "tarball": "http://registry.npmjs.org/har-validator/-/har-validator-1.7.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/har-validator/-/har-validator-1.7.0.tgz"
+}
--- /dev/null
+'use strict'
+
+function ValidationError (errors) {
+ this.name = 'ValidationError'
+ this.errors = errors
+}
+
+ValidationError.prototype = Error.prototype
+
+module.exports = ValidationError
--- /dev/null
+'use strict'
+
+var schemas = require('./schemas')
+var ValidationError = require('./error')
+var validator = require('is-my-json-valid')
+
+var runner = function (schema, data, cb) {
+ var validate = validator(schema, {
+ greedy: true,
+ verbose: true,
+ schemas: schemas
+ })
+
+ var valid = false
+
+ if (data !== undefined) {
+ // execute is-my-json-valid
+ valid = validate(data)
+ }
+
+ // callback?
+ if (!cb) {
+ return validate.errors ? false : true
+ } else {
+ return cb(validate.errors ? new ValidationError(validate.errors) : null, valid)
+ }
+
+ return valid
+}
+
+module.exports = function (data, cb) {
+ return runner(schemas.har, data, cb)
+}
+
+Object.keys(schemas).map(function (name) {
+ module.exports[name] = function (data, cb) {
+ return runner(schemas[name], data, cb)
+ }
+})
--- /dev/null
+{
+ "properties": {
+ "beforeRequest": {
+ "$ref": "#cacheEntry"
+ },
+ "afterRequest": {
+ "$ref": "#cacheEntry"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "oneOf": [{
+ "type": "object",
+ "optional": true,
+ "required": [
+ "lastAccess",
+ "eTag",
+ "hitCount"
+ ],
+ "properties": {
+ "expires": {
+ "type": "string"
+ },
+ "lastAccess": {
+ "type": "string"
+ },
+ "eTag": {
+ "type": "string"
+ },
+ "hitCount": {
+ "type": "integer"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+ }, {
+ "type": null,
+ "additionalProperties": false
+ }]
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "size",
+ "mimeType"
+ ],
+ "properties": {
+ "size": {
+ "type": "integer"
+ },
+ "compression": {
+ "type": "integer"
+ },
+ "mimeType": {
+ "type": "string"
+ },
+ "text": {
+ "type": "string"
+ },
+ "encoding": {
+ "type": "string"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "path": {
+ "type": "string"
+ },
+ "domain": {
+ "type": "string"
+ },
+ "expires": {
+ "type": ["string", "null"],
+ "format": "date-time"
+ },
+ "httpOnly": {
+ "type": "boolean"
+ },
+ "secure": {
+ "type": "boolean"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "name",
+ "version"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "optional": true,
+ "required": [
+ "startedDateTime",
+ "time",
+ "request",
+ "response",
+ "cache",
+ "timings"
+ ],
+ "properties": {
+ "pageref": {
+ "type": "string"
+ },
+ "startedDateTime": {
+ "type": "string",
+ "format": "date-time",
+ "pattern": "^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))"
+ },
+ "time": {
+ "type": "number",
+ "min": 0
+ },
+ "request": {
+ "$ref": "#request"
+ },
+ "response": {
+ "$ref": "#response"
+ },
+ "cache": {
+ "$ref": "#cache"
+ },
+ "timings": {
+ "$ref": "#timings"
+ },
+ "serverIPAddress": {
+ "type": "string",
+ "format": "ipv4"
+ },
+ "connection": {
+ "type": "string"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "log"
+ ],
+ "properties": {
+ "log": {
+ "$ref": "#log"
+ }
+ }
+}
--- /dev/null
+'use strict'
+
+var schemas = {
+ cache: require('./cache.json'),
+ cacheEntry: require('./cacheEntry.json'),
+ content: require('./content.json'),
+ cookie: require('./cookie.json'),
+ creator: require('./creator.json'),
+ entry: require('./entry.json'),
+ har: require('./har.json'),
+ log: require('./log.json'),
+ page: require('./page.json'),
+ pageTimings: require('./pageTimings.json'),
+ postData: require('./postData.json'),
+ record: require('./record.json'),
+ request: require('./request.json'),
+ response: require('./response.json'),
+ timings: require('./timings.json')
+}
+
+// is-my-json-valid does not provide meaningful error messages for external schemas
+// this is a workaround
+schemas.cache.properties.beforeRequest = schemas.cacheEntry
+schemas.cache.properties.afterRequest = schemas.cacheEntry
+
+schemas.page.properties.pageTimings = schemas.pageTimings
+
+schemas.request.properties.cookies.items = schemas.cookie
+schemas.request.properties.headers.items = schemas.record
+schemas.request.properties.queryString.items = schemas.record
+schemas.request.properties.postData = schemas.postData
+
+schemas.response.properties.cookies.items = schemas.cookie
+schemas.response.properties.headers.items = schemas.record
+schemas.response.properties.content = schemas.content
+
+schemas.entry.properties.request = schemas.request
+schemas.entry.properties.response = schemas.response
+schemas.entry.properties.cache = schemas.cache
+schemas.entry.properties.timings = schemas.timings
+
+schemas.log.properties.creator = schemas.creator
+schemas.log.properties.browser = schemas.creator
+schemas.log.properties.pages.items = schemas.page
+schemas.log.properties.entries.items = schemas.entry
+
+schemas.har.properties.log = schemas.log
+
+module.exports = schemas
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "version",
+ "creator",
+ "entries"
+ ],
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+ "creator": {
+ "$ref": "#creator"
+ },
+ "browser": {
+ "$ref": "#creator"
+ },
+ "pages": {
+ "type": "array",
+ "items": {
+ "$ref": "#page"
+ }
+ },
+ "entries": {
+ "type": "array",
+ "items": {
+ "$ref": "#entry"
+ }
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "optional": true,
+ "required": [
+ "startedDateTime",
+ "id",
+ "title",
+ "pageTimings"
+ ],
+ "properties": {
+ "startedDateTime": {
+ "type": "string",
+ "format": "date-time",
+ "pattern": "^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))"
+ },
+ "id": {
+ "type": "string",
+ "unique": true
+ },
+ "title": {
+ "type": "string"
+ },
+ "pageTimings": {
+ "$ref": "#pageTimings"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "properties": {
+ "onContentLoad": {
+ "type": "number",
+ "min": -1
+ },
+ "onLoad": {
+ "type": "number",
+ "min": -1
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "optional": true,
+ "required": [
+ "mimeType"
+ ],
+ "properties": {
+ "mimeType": {
+ "type": "string"
+ },
+ "text": {
+ "type": "string"
+ },
+ "params": {
+ "type": "array",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "fileName": {
+ "type": "string"
+ },
+ "contentType": {
+ "type": "string"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "method",
+ "url",
+ "httpVersion",
+ "cookies",
+ "headers",
+ "queryString",
+ "headersSize",
+ "bodySize"
+ ],
+ "properties": {
+ "method": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "httpVersion": {
+ "type": "string"
+ },
+ "cookies": {
+ "type": "array",
+ "items": {
+ "$ref": "#cookie"
+ }
+ },
+ "headers": {
+ "type": "array",
+ "items": {
+ "$ref": "#record"
+ }
+ },
+ "queryString": {
+ "type": "array",
+ "items": {
+ "$ref": "#record"
+ }
+ },
+ "postData": {
+ "$ref": "#postData"
+ },
+ "headersSize": {
+ "type": "integer"
+ },
+ "bodySize": {
+ "type": "integer"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "type": "object",
+ "required": [
+ "status",
+ "statusText",
+ "httpVersion",
+ "cookies",
+ "headers",
+ "content",
+ "redirectURL",
+ "headersSize",
+ "bodySize"
+ ],
+ "properties": {
+ "status": {
+ "type": "integer"
+ },
+ "statusText": {
+ "type": "string"
+ },
+ "httpVersion": {
+ "type": "string"
+ },
+ "cookies": {
+ "type": "array",
+ "items": {
+ "$ref": "#cookie"
+ }
+ },
+ "headers": {
+ "type": "array",
+ "items": {
+ "$ref": "#record"
+ }
+ },
+ "content": {
+ "$ref": "#content"
+ },
+ "redirectURL": {
+ "type": "string"
+ },
+ "headersSize": {
+ "type": "integer"
+ },
+ "bodySize": {
+ "type": "integer"
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+{
+ "required": [
+ "send",
+ "wait",
+ "receive"
+ ],
+ "properties": {
+ "dns": {
+ "type": "number",
+ "min": -1
+ },
+ "connect": {
+ "type": "number",
+ "min": -1
+ },
+ "blocked": {
+ "type": "number",
+ "min": -1
+ },
+ "send": {
+ "type": "number",
+ "min": -1
+ },
+ "wait": {
+ "type": "number",
+ "min": -1
+ },
+ "receive": {
+ "type": "number",
+ "min": -1
+ },
+ "ssl": {
+ "type": "number",
+ "min": -1
+ },
+ "comment": {
+ "type": "string"
+ }
+ }
+}
--- /dev/null
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+components
+build
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+
--- /dev/null
+language: node_js
+
+node_js:
+ - 0.10
+
--- /dev/null
+Copyright (c) 2012-2014, Eran Hammer and other contributors.
+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.
+ * The names of any contributors may not 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS 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.
+
+ * * *
+
+The complete list of contributors can be found at: https://github.com/hueniverse/hawk/graphs/contributors
--- /dev/null
+test:
+ @node node_modules/lab/bin/lab -a code
+test-cov:
+ @node node_modules/lab/bin/lab -a code -t 100
+test-cov-html:
+ @node node_modules/lab/bin/lab -a code -r html -o coverage.html
+
+.PHONY: test test-cov test-cov-html
--- /dev/null
+
+
+<img align="right" src="https://raw.github.com/hueniverse/hawk/master/images/logo.png" /> **Hawk** is an HTTP authentication scheme using a message authentication code (MAC) algorithm to provide partial
+HTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](https://github.com/hueniverse/oz).
+
+Current version: **2.3**
+
+Note: 2.x is the same exact protocol as 1.1. The version increment reflects a change in the internal error format
+used by the module and used by the node API.
+
+[](http://travis-ci.org/hueniverse/hawk)
+
+# Table of Content
+
+- [**Introduction**](#introduction)
+ - [Replay Protection](#replay-protection)
+ - [Usage Example](#usage-example)
+ - [Protocol Example](#protocol-example)
+ - [Payload Validation](#payload-validation)
+ - [Response Payload Validation](#response-payload-validation)
+ - [Browser Support and Considerations](#browser-support-and-considerations)
+<p></p>
+- [**Single URI Authorization**](#single-uri-authorization)
+ - [Usage Example](#bewit-usage-example)
+<p></p>
+- [**Security Considerations**](#security-considerations)
+ - [MAC Keys Transmission](#mac-keys-transmission)
+ - [Confidentiality of Requests](#confidentiality-of-requests)
+ - [Spoofing by Counterfeit Servers](#spoofing-by-counterfeit-servers)
+ - [Plaintext Storage of Credentials](#plaintext-storage-of-credentials)
+ - [Entropy of Keys](#entropy-of-keys)
+ - [Coverage Limitations](#coverage-limitations)
+ - [Future Time Manipulation](#future-time-manipulation)
+ - [Client Clock Poisoning](#client-clock-poisoning)
+ - [Bewit Limitations](#bewit-limitations)
+ - [Host Header Forgery](#host-header-forgery)
+<p></p>
+- [**Frequently Asked Questions**](#frequently-asked-questions)
+<p></p>
+- [**Implementations**](#implementations)
+- [**Acknowledgements**](#acknowledgements)
+
+# Introduction
+
+**Hawk** is an HTTP authentication scheme providing mechanisms for making authenticated HTTP requests with
+partial cryptographic verification of the request and response, covering the HTTP method, request URI, host,
+and optionally the request payload.
+
+Similar to the HTTP [Digest access authentication schemes](http://www.ietf.org/rfc/rfc2617.txt), **Hawk** uses a set of
+client credentials which include an identifier (e.g. username) and key (e.g. password). Likewise, just as with the Digest scheme,
+the key is never included in authenticated requests. Instead, it is used to calculate a request MAC value which is
+included in its place.
+
+However, **Hawk** has several differences from Digest. In particular, while both use a nonce to limit the possibility of
+replay attacks, in **Hawk** the client generates the nonce and uses it in combination with a timestamp, leading to less
+"chattiness" (interaction with the server).
+
+Also unlike Digest, this scheme is not intended to protect the key itself (the password in Digest) because
+the client and server must both have access to the key material in the clear.
+
+The primary design goals of this scheme are to:
+* simplify and improve HTTP authentication for services that are unwilling or unable to deploy TLS for all resources,
+* secure credentials against leakage (e.g., when the client uses some form of dynamic configuration to determine where
+ to send an authenticated request), and
+* avoid the exposure of credentials sent to a malicious server over an unauthenticated secure channel due to client
+ failure to validate the server's identity as part of its TLS handshake.
+
+In addition, **Hawk** supports a method for granting third-parties temporary access to individual resources using
+a query parameter called _bewit_ (in falconry, a leather strap used to attach a tracking device to the leg of a hawk).
+
+The **Hawk** scheme requires the establishment of a shared symmetric key between the client and the server,
+which is beyond the scope of this module. Typically, the shared credentials are established via an initial
+TLS-protected phase or derived from some other shared confidential information available to both the client
+and the server.
+
+
+## Replay Protection
+
+Without replay protection, an attacker can use a compromised (but otherwise valid and authenticated) request more
+than once, gaining access to a protected resource. To mitigate this, clients include both a nonce and a timestamp when
+making requests. This gives the server enough information to prevent replay attacks.
+
+The nonce is generated by the client, and is a string unique across all requests with the same timestamp and
+key identifier combination.
+
+The timestamp enables the server to restrict the validity period of the credentials where requests occuring afterwards
+are rejected. It also removes the need for the server to retain an unbounded number of nonce values for future checks.
+By default, **Hawk** uses a time window of 1 minute to allow for time skew between the client and server (which in
+practice translates to a maximum of 2 minutes as the skew can be positive or negative).
+
+Using a timestamp requires the client's clock to be in sync with the server's clock. **Hawk** requires both the client
+clock and the server clock to use NTP to ensure synchronization. However, given the limitations of some client types
+(e.g. browsers) to deploy NTP, the server provides the client with its current time (in seconds precision) in response
+to a bad timestamp.
+
+There is no expectation that the client will adjust its system clock to match the server (in fact, this would be a
+potential attack vector). Instead, the client only uses the server's time to calculate an offset used only
+for communications with that particular server. The protocol rewards clients with synchronized clocks by reducing
+the number of round trips required to authenticate the first request.
+
+
+## Usage Example
+
+Server code:
+
+```javascript
+var Http = require('http');
+var Hawk = require('hawk');
+
+
+// Credentials lookup function
+
+var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'Steve'
+ };
+
+ return callback(null, credentials);
+};
+
+// Create HTTP server
+
+var handler = function (req, res) {
+
+ // Authenticate incoming request
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ // Prepare response
+
+ var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');
+ var headers = { 'Content-Type': 'text/plain' };
+
+ // Generate Server-Authorization response header
+
+ var header = Hawk.server.header(credentials, artifacts, { payload: payload, contentType: headers['Content-Type'] });
+ headers['Server-Authorization'] = header;
+
+ // Send the response back
+
+ res.writeHead(!err ? 200 : 401, headers);
+ res.end(payload);
+ });
+};
+
+// Start server
+
+Http.createServer(handler).listen(8000, 'example.com');
+```
+
+Client code:
+
+```javascript
+var Request = require('request');
+var Hawk = require('hawk');
+
+
+// Client credentials
+
+var credentials = {
+ id: 'dh37fgj492je',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256'
+}
+
+// Request options
+
+var requestOptions = {
+ uri: 'http://example.com:8000/resource/1?b=1&a=2',
+ method: 'GET',
+ headers: {}
+};
+
+// Generate Authorization request header
+
+var header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });
+requestOptions.headers.Authorization = header.field;
+
+// Send authenticated request
+
+Request(requestOptions, function (error, response, body) {
+
+ // Authenticate the server's response
+
+ var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
+
+ // Output results
+
+ console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
+});
+```
+
+**Hawk** utilized the [**SNTP**](https://github.com/hueniverse/sntp) module for time sync management. By default, the local
+machine time is used. To automatically retrieve and synchronice the clock within the application, use the SNTP 'start()' method.
+
+```javascript
+Hawk.sntp.start();
+```
+
+
+## Protocol Example
+
+The client attempts to access a protected resource without authentication, sending the following HTTP request to
+the resource server:
+
+```
+GET /resource/1?b=1&a=2 HTTP/1.1
+Host: example.com:8000
+```
+
+The resource server returns an authentication challenge.
+
+```
+HTTP/1.1 401 Unauthorized
+WWW-Authenticate: Hawk
+```
+
+The client has previously obtained a set of **Hawk** credentials for accessing resources on the "http://example.com/"
+server. The **Hawk** credentials issued to the client include the following attributes:
+
+* Key identifier: dh37fgj492je
+* Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn
+* Algorithm: sha256
+
+The client generates the authentication header by calculating a timestamp (e.g. the number of seconds since January 1,
+1970 00:00:00 GMT), generating a nonce, and constructing the normalized request string (each value followed by a newline
+character):
+
+```
+hawk.1.header
+1353832234
+j4h3g2
+GET
+/resource/1?b=1&a=2
+example.com
+8000
+
+some-app-ext-data
+
+```
+
+The request MAC is calculated using HMAC with the specified hash algorithm "sha256" and the key over the normalized request string.
+The result is base64-encoded to produce the request MAC:
+
+```
+6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=
+```
+
+The client includes the **Hawk** key identifier, timestamp, nonce, application specific data, and request MAC with the request using
+the HTTP `Authorization` request header field:
+
+```
+GET /resource/1?b=1&a=2 HTTP/1.1
+Host: example.com:8000
+Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="
+```
+
+The server validates the request by calculating the request MAC again based on the request received and verifies the validity
+and scope of the **Hawk** credentials. If valid, the server responds with the requested resource.
+
+
+### Payload Validation
+
+**Hawk** provides optional payload validation. When generating the authentication header, the client calculates a payload hash
+using the specified hash algorithm. The hash is calculated over the concatenated value of (each followed by a newline character):
+* `hawk.1.payload`
+* the content-type in lowercase, without any parameters (e.g. `application/json`)
+* the request payload prior to any content encoding (the exact representation requirements should be specified by the server for payloads other than simple single-part ascii to ensure interoperability)
+
+For example:
+
+* Payload: `Thank you for flying Hawk`
+* Content Type: `text/plain`
+* Hash (sha256): `Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=`
+
+Results in the following input to the payload hash function (newline terminated values):
+
+```
+hawk.1.payload
+text/plain
+Thank you for flying Hawk
+
+```
+
+Which produces the following hash value:
+
+```
+Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=
+```
+
+The client constructs the normalized request string (newline terminated values):
+
+```
+hawk.1.header
+1353832234
+j4h3g2
+POST
+/resource/1?a=1&b=2
+example.com
+8000
+Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=
+some-app-ext-data
+
+```
+
+Then calculates the request MAC and includes the **Hawk** key identifier, timestamp, nonce, payload hash, application specific data,
+and request MAC, with the request using the HTTP `Authorization` request header field:
+
+```
+POST /resource/1?a=1&b=2 HTTP/1.1
+Host: example.com:8000
+Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="
+```
+
+It is up to the server if and when it validates the payload for any given request, based solely on it's security policy
+and the nature of the data included.
+
+If the payload is available at the time of authentication, the server uses the hash value provided by the client to construct
+the normalized string and validates the MAC. If the MAC is valid, the server calculates the payload hash and compares the value
+with the provided payload hash in the header. In many cases, checking the MAC first is faster than calculating the payload hash.
+
+However, if the payload is not available at authentication time (e.g. too large to fit in memory, streamed elsewhere, or processed
+at a different stage in the application), the server may choose to defer payload validation for later by retaining the hash value
+provided by the client after validating the MAC.
+
+It is important to note that MAC validation does not mean the hash value provided by the client is valid, only that the value
+included in the header was not modified. Without calculating the payload hash on the server and comparing it to the value provided
+by the client, the payload may be modified by an attacker.
+
+
+## Response Payload Validation
+
+**Hawk** provides partial response payload validation. The server includes the `Server-Authorization` response header which enables the
+client to authenticate the response and ensure it is talking to the right server. **Hawk** defines the HTTP `Server-Authorization` header
+as a response header using the exact same syntax as the `Authorization` request header field.
+
+The header is contructed using the same process as the client's request header. The server uses the same credentials and other
+artifacts provided by the client to constructs the normalized request string. The `ext` and `hash` values are replaced with
+new values based on the server response. The rest as identical to those used by the client.
+
+The result MAC digest is included with the optional `hash` and `ext` values:
+
+```
+Server-Authorization: Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"
+```
+
+
+## Browser Support and Considerations
+
+A browser script is provided for including using a `<script>` tag in [lib/browser.js](/lib/browser.js). It's also a [component](http://component.io/hueniverse/hawk).
+
+**Hawk** relies on the _Server-Authorization_ and _WWW-Authenticate_ headers in its response to communicate with the client.
+Therefore, in case of CORS requests, it is important to consider sending _Access-Control-Expose-Headers_ with the value
+_"WWW-Authenticate, Server-Authorization"_ on each response from your server. As explained in the
+[specifications](http://www.w3.org/TR/cors/#access-control-expose-headers-response-header), it will indicate that these headers
+can safely be accessed by the client (using getResponseHeader() on the XmlHttpRequest object). Otherwise you will be met with a
+["simple response header"](http://www.w3.org/TR/cors/#simple-response-header) which excludes these fields and would prevent the
+Hawk client from authenticating the requests.You can read more about the why and how in this
+[article](http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server)
+
+
+# Single URI Authorization
+
+There are cases in which limited and short-term access to a protected resource is granted to a third party which does not
+have access to the shared credentials. For example, displaying a protected image on a web page accessed by anyone. **Hawk**
+provides limited support for such URIs in the form of a _bewit_ - a URI query parameter appended to the request URI which contains
+the necessary credentials to authenticate the request.
+
+Because of the significant security risks involved in issuing such access, bewit usage is purposely limited only to GET requests
+and for a finite period of time. Both the client and server can issue bewit credentials, however, the server should not use the same
+credentials as the client to maintain clear traceability as to who issued which credentials.
+
+In order to simplify implementation, bewit credentials do not support single-use policy and can be replayed multiple times within
+the granted access timeframe.
+
+
+## Bewit Usage Example
+
+Server code:
+
+```javascript
+var Http = require('http');
+var Hawk = require('hawk');
+
+
+// Credentials lookup function
+
+var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256'
+ };
+
+ return callback(null, credentials);
+};
+
+// Create HTTP server
+
+var handler = function (req, res) {
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ res.writeHead(!err ? 200 : 401, { 'Content-Type': 'text/plain' });
+ res.end(!err ? 'Access granted' : 'Shoosh!');
+ });
+};
+
+Http.createServer(handler).listen(8000, 'example.com');
+```
+
+Bewit code generation:
+
+```javascript
+var Request = require('request');
+var Hawk = require('hawk');
+
+
+// Client credentials
+
+var credentials = {
+ id: 'dh37fgj492je',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256'
+}
+
+// Generate bewit
+
+var duration = 60 * 5; // 5 Minutes
+var bewit = Hawk.uri.getBewit('http://example.com:8080/resource/1?b=1&a=2', { credentials: credentials, ttlSec: duration, ext: 'some-app-data' });
+var uri = 'http://example.com:8000/resource/1?b=1&a=2' + '&bewit=' + bewit;
+```
+
+
+# Security Considerations
+
+The greatest sources of security risks are usually found not in **Hawk** but in the policies and procedures surrounding its use.
+Implementers are strongly encouraged to assess how this module addresses their security requirements. This section includes
+an incomplete list of security considerations that must be reviewed and understood before deploying **Hawk** on the server.
+Many of the protections provided in **Hawk** depends on whether and how they are used.
+
+### MAC Keys Transmission
+
+**Hawk** does not provide any mechanism for obtaining or transmitting the set of shared credentials required. Any mechanism used
+to obtain **Hawk** credentials must ensure that these transmissions are protected using transport-layer mechanisms such as TLS.
+
+### Confidentiality of Requests
+
+While **Hawk** provides a mechanism for verifying the integrity of HTTP requests, it provides no guarantee of request
+confidentiality. Unless other precautions are taken, eavesdroppers will have full access to the request content. Servers should
+carefully consider the types of data likely to be sent as part of such requests, and employ transport-layer security mechanisms
+to protect sensitive resources.
+
+### Spoofing by Counterfeit Servers
+
+**Hawk** provides limited verification of the server authenticity. When receiving a response back from the server, the server
+may choose to include a response `Server-Authorization` header which the client can use to verify the response. However, it is up to
+the server to determine when such measure is included, to up to the client to enforce that policy.
+
+A hostile party could take advantage of this by intercepting the client's requests and returning misleading or otherwise
+incorrect responses. Service providers should consider such attacks when developing services using this protocol, and should
+require transport-layer security for any requests where the authenticity of the resource server or of server responses is an issue.
+
+### Plaintext Storage of Credentials
+
+The **Hawk** key functions the same way passwords do in traditional authentication systems. In order to compute the request MAC,
+the server must have access to the key in plaintext form. This is in contrast, for example, to modern operating systems, which
+store only a one-way hash of user credentials.
+
+If an attacker were to gain access to these keys - or worse, to the server's database of all such keys - he or she would be able
+to perform any action on behalf of any resource owner. Accordingly, it is critical that servers protect these keys from unauthorized
+access.
+
+### Entropy of Keys
+
+Unless a transport-layer security protocol is used, eavesdroppers will have full access to authenticated requests and request
+MAC values, and will thus be able to mount offline brute-force attacks to recover the key used. Servers should be careful to
+assign keys which are long enough, and random enough, to resist such attacks for at least the length of time that the **Hawk**
+credentials are valid.
+
+For example, if the credentials are valid for two weeks, servers should ensure that it is not possible to mount a brute force
+attack that recovers the key in less than two weeks. Of course, servers are urged to err on the side of caution, and use the
+longest key reasonable.
+
+It is equally important that the pseudo-random number generator (PRNG) used to generate these keys be of sufficiently high
+quality. Many PRNG implementations generate number sequences that may appear to be random, but which nevertheless exhibit
+patterns or other weaknesses which make cryptanalysis or brute force attacks easier. Implementers should be careful to use
+cryptographically secure PRNGs to avoid these problems.
+
+### Coverage Limitations
+
+The request MAC only covers the HTTP `Host` header and optionally the `Content-Type` header. It does not cover any other headers
+which can often affect how the request body is interpreted by the server. If the server behavior is influenced by the presence
+or value of such headers, an attacker can manipulate the request headers without being detected. Implementers should use the
+`ext` feature to pass application-specific information via the `Authorization` header which is protected by the request MAC.
+
+The response authentication, when performed, only covers the response payload, content-type, and the request information
+provided by the client in it's request (method, resource, timestamp, nonce, etc.). It does not cover the HTTP status code or
+any other response header field (e.g. Location) which can affect the client's behaviour.
+
+### Future Time Manipulation
+
+The protocol relies on a clock sync between the client and server. To accomplish this, the server informs the client of its
+current time when an invalid timestamp is received.
+
+If an attacker is able to manipulate this information and cause the client to use an incorrect time, it would be able to cause
+the client to generate authenticated requests using time in the future. Such requests will fail when sent by the client, and will
+not likely leave a trace on the server (given the common implementation of nonce, if at all enforced). The attacker will then
+be able to replay the request at the correct time without detection.
+
+The client must only use the time information provided by the server if:
+* it was delivered over a TLS connection and the server identity has been verified, or
+* the `tsm` MAC digest calculated using the same client credentials over the timestamp has been verified.
+
+### Client Clock Poisoning
+
+When receiving a request with a bad timestamp, the server provides the client with its current time. The client must never use
+the time received from the server to adjust its own clock, and must only use it to calculate an offset for communicating with
+that particular server.
+
+### Bewit Limitations
+
+Special care must be taken when issuing bewit credentials to third parties. Bewit credentials are valid until expiration and cannot
+be revoked or limited without using other means. Whatever resource they grant access to will be completely exposed to anyone with
+access to the bewit credentials which act as bearer credentials for that particular resource. While bewit usage is limited to GET
+requests only and therefore cannot be used to perform transactions or change server state, it can still be used to expose private
+and sensitive information.
+
+### Host Header Forgery
+
+Hawk validates the incoming request MAC against the incoming HTTP Host header. However, unless the optional `host` and `port`
+options are used with `server.authenticate()`, a malicous client can mint new host names pointing to the server's IP address and
+use that to craft an attack by sending a valid request that's meant for another hostname than the one used by the server. Server
+implementors must manually verify that the host header received matches their expectation (or use the options mentioned above).
+
+# Frequently Asked Questions
+
+### Where is the protocol specification?
+
+If you are looking for some prose explaining how all this works, **this is it**. **Hawk** is being developed as an open source
+project instead of a standard. In other words, the [code](/hueniverse/hawk/tree/master/lib) is the specification. Not sure about
+something? Open an issue!
+
+### Is it done?
+
+As of version 0.10.0, **Hawk** is feature-complete. However, until this module reaches version 1.0.0 it is considered experimental
+and is likely to change. This also means your feedback and contribution are very welcome. Feel free to open issues with questions
+and suggestions.
+
+### Where can I find **Hawk** implementations in other languages?
+
+**Hawk**'s only reference implementation is provided in JavaScript as a node.js module. However, it has been ported to other languages.
+The full list is maintained [here](https://github.com/hueniverse/hawk/issues?labels=port&state=closed). Please add an issue if you are
+working on another port. A cross-platform test-suite is in the works.
+
+### Why isn't the algorithm part of the challenge or dynamically negotiated?
+
+The algorithm used is closely related to the key issued as different algorithms require different key sizes (and other
+requirements). While some keys can be used for multiple algorithm, the protocol is designed to closely bind the key and algorithm
+together as part of the issued credentials.
+
+### Why is Host and Content-Type the only headers covered by the request MAC?
+
+It is really hard to include other headers. Headers can be changed by proxies and other intermediaries and there is no
+well-established way to normalize them. Many platforms change the case of header field names and values. The only
+straight-forward solution is to include the headers in some blob (say, base64 encoded JSON) and include that with the request,
+an approach taken by JWT and other such formats. However, that design violates the HTTP header boundaries, repeats information,
+and introduces other security issues because firewalls will not be aware of these "hidden" headers. In addition, any information
+repeated must be compared to the duplicated information in the header and therefore only moves the problem elsewhere.
+
+### Why not just use HTTP Digest?
+
+Digest requires pre-negotiation to establish a nonce. This means you can't just make a request - you must first send
+a protocol handshake to the server. This pattern has become unacceptable for most web services, especially mobile
+where extra round-trip are costly.
+
+### Why bother with all this nonce and timestamp business?
+
+**Hawk** is an attempt to find a reasonable, practical compromise between security and usability. OAuth 1.0 got timestamp
+and nonces halfway right but failed when it came to scalability and consistent developer experience. **Hawk** addresses
+it by requiring the client to sync its clock, but provides it with tools to accomplish it.
+
+In general, replay protection is a matter of application-specific threat model. It is less of an issue on a TLS-protected
+system where the clients are implemented using best practices and are under the control of the server. Instead of dropping
+replay protection, **Hawk** offers a required time window and an optional nonce verification. Together, it provides developers
+with the ability to decide how to enforce their security policy without impacting the client's implementation.
+
+### What are `app` and `dlg` in the authorization header and normalized mac string?
+
+The original motivation for **Hawk** was to replace the OAuth 1.0 use cases. This included both a simple client-server mode which
+this module is specifically designed for, and a delegated access mode which is being developed separately in
+[Oz](https://github.com/hueniverse/oz). In addition to the **Hawk** use cases, Oz requires another attribute: the application id `app`.
+This provides binding between the credentials and the application in a way that prevents an attacker from tricking an application
+to use credentials issued to someone else. It also has an optional 'delegated-by' attribute `dlg` which is the application id of the
+application the credentials were directly issued to. The goal of these two additions is to allow Oz to utilize **Hawk** directly,
+but with the additional security of delegated credentials.
+
+### What is the purpose of the static strings used in each normalized MAC input?
+
+When calculating a hash or MAC, a static prefix (tag) is added. The prefix is used to prevent MAC values from being
+used or reused for a purpose other than what they were created for (i.e. prevents switching MAC values between a request,
+response, and a bewit use cases). It also protects against exploits created after a potential change in how the protocol
+creates the normalized string. For example, if a future version would switch the order of nonce and timestamp, it
+can create an exploit opportunity for cases where the nonce is similar in format to a timestamp.
+
+### Does **Hawk** have anything to do with OAuth?
+
+Short answer: no.
+
+**Hawk** was originally proposed as the OAuth MAC Token specification. However, the OAuth working group in its consistent
+incompetence failed to produce a final, usable solution to address one of the most popular use cases of OAuth 1.0 - using it
+to authenticate simple client-server transactions (i.e. two-legged). As you can guess, the OAuth working group is still hard
+at work to produce more garbage.
+
+**Hawk** provides a simple HTTP authentication scheme for making client-server requests. It does not address the OAuth use case
+of delegating access to a third party. If you are looking for an OAuth alternative, check out [Oz](https://github.com/hueniverse/oz).
+
+# Implementations
+
+- [Logibit Hawk in F#/.Net](https://github.com/logibit/logibit.hawk/)
+- [Tent Hawk in Ruby](https://github.com/tent/hawk-ruby)
+- [Wealdtech in Java](https://github.com/wealdtech/hawk)
+- [Kumar's Mohawk in Python](https://github.com/kumar303/mohawk/)
+
+# Acknowledgements
+
+**Hawk** is a derivative work of the [HTTP MAC Authentication Scheme](http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05) proposal
+co-authored by Ben Adida, Adam Barth, and Eran Hammer, which in turn was based on the OAuth 1.0 community specification.
+
+Special thanks to Ben Laurie for his always insightful feedback and advice.
+
+The **Hawk** logo was created by [Chris Carrasco](http://chriscarrasco.com).
--- /dev/null
+{
+ "name": "hawk",
+ "main": "lib/browser.js",
+ "license": "./LICENSE",
+ "ignore": [
+ "!lib",
+ "lib/*",
+ "!lib/browser.js",
+ "index.js"
+ ],
+ "keywords": [
+ "http",
+ "authentication",
+ "scheme",
+ "hawk"
+ ],
+ "authors": [
+ "Eran Hammer <eran@hammer.io>"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/hueniverse/hawk.git"
+ }
+}
--- /dev/null
+{
+ "name": "hawk",
+ "repo": "hueniverse/hawk",
+ "description": "HTTP Hawk Authentication Scheme",
+ "version": "1.0.0",
+ "keywords": [
+ "http",
+ "authentication",
+ "scheme",
+ "hawk"
+ ],
+ "dependencies": {},
+ "development": {},
+ "license": "BSD",
+ "main": "lib/browser.js",
+ "scripts": [
+ "lib/browser.js"
+ ]
+}
\ No newline at end of file
--- /dev/null
+// Load modules
+
+var Http = require('http');
+var Request = require('request');
+var Hawk = require('../lib');
+
+
+// Declare internals
+
+var internals = {
+ credentials: {
+ dh37fgj492je: {
+ id: 'dh37fgj492je', // Required by Hawk.client.header
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'Steve'
+ }
+ }
+};
+
+
+// Credentials lookup function
+
+var credentialsFunc = function (id, callback) {
+
+ return callback(null, internals.credentials[id]);
+};
+
+
+// Create HTTP server
+
+var handler = function (req, res) {
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');
+ var headers = {
+ 'Content-Type': 'text/plain',
+ 'Server-Authorization': Hawk.server.header(credentials, artifacts, { payload: payload, contentType: 'text/plain' })
+ };
+
+ res.writeHead(!err ? 200 : 401, headers);
+ res.end(payload);
+ });
+};
+
+Http.createServer(handler).listen(8000, '127.0.0.1');
+
+
+// Send unauthenticated request
+
+Request('http://127.0.0.1:8000/resource/1?b=1&a=2', function (error, response, body) {
+
+ console.log(response.statusCode + ': ' + body);
+});
+
+
+// Send authenticated request
+
+credentialsFunc('dh37fgj492je', function (err, credentials) {
+
+ var header = Hawk.client.header('http://127.0.0.1:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'and welcome!' });
+ var options = {
+ uri: 'http://127.0.0.1:8000/resource/1?b=1&a=2',
+ method: 'GET',
+ headers: {
+ authorization: header.field
+ }
+ };
+
+ Request(options, function (error, response, body) {
+
+ var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
+ console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
+ process.exit(0);
+ });
+});
+
--- /dev/null
+module.exports = require('./lib');
\ No newline at end of file
--- /dev/null
+/*
+ HTTP Hawk Authentication Scheme
+ Copyright (c) 2012-2014, Eran Hammer <eran@hammer.io>
+ BSD Licensed
+*/
+
+
+// Declare namespace
+
+var hawk = {
+ internals: {}
+};
+
+
+hawk.client = {
+
+ // Generate an Authorization header for a given request
+
+ /*
+ uri: 'http://example.com/resource?a=b' or object generated by hawk.utils.parseUri()
+ method: HTTP verb (e.g. 'GET', 'POST')
+ options: {
+
+ // Required
+
+ credentials: {
+ id: 'dh37fgj492je',
+ key: 'aoijedoaijsdlaksjdl',
+ algorithm: 'sha256' // 'sha1', 'sha256'
+ },
+
+ // Optional
+
+ ext: 'application-specific', // Application specific data sent via the ext attribute
+ timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
+ nonce: '2334f34f', // A pre-generated nonce
+ localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
+ payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
+ contentType: 'application/json', // Payload content-type (ignored if hash provided)
+ hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
+ app: '24s23423f34dx', // Oz application id
+ dlg: '234sz34tww3sd' // Oz delegated-by application id
+ }
+ */
+
+ header: function (uri, method, options) {
+
+ var result = {
+ field: '',
+ artifacts: {}
+ };
+
+ // Validate inputs
+
+ if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
+ !method || typeof method !== 'string' ||
+ !options || typeof options !== 'object') {
+
+ result.err = 'Invalid argument type';
+ return result;
+ }
+
+ // Application time
+
+ var timestamp = options.timestamp || hawk.utils.now(options.localtimeOffsetMsec);
+
+ // Validate credentials
+
+ var credentials = options.credentials;
+ if (!credentials ||
+ !credentials.id ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ result.err = 'Invalid credentials object';
+ return result;
+ }
+
+ if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ result.err = 'Unknown algorithm';
+ return result;
+ }
+
+ // Parse URI
+
+ if (typeof uri === 'string') {
+ uri = hawk.utils.parseUri(uri);
+ }
+
+ // Calculate signature
+
+ var artifacts = {
+ ts: timestamp,
+ nonce: options.nonce || hawk.utils.randomString(6),
+ method: method,
+ resource: uri.relative,
+ host: uri.hostname,
+ port: uri.port,
+ hash: options.hash,
+ ext: options.ext,
+ app: options.app,
+ dlg: options.dlg
+ };
+
+ result.artifacts = artifacts;
+
+ // Calculate payload hash
+
+ if (!artifacts.hash &&
+ (options.payload || options.payload === '')) {
+
+ artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
+ }
+
+ var mac = hawk.crypto.calculateMac('header', credentials, artifacts);
+
+ // Construct header
+
+ var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
+ var header = 'Hawk id="' + credentials.id +
+ '", ts="' + artifacts.ts +
+ '", nonce="' + artifacts.nonce +
+ (artifacts.hash ? '", hash="' + artifacts.hash : '') +
+ (hasExt ? '", ext="' + hawk.utils.escapeHeaderAttribute(artifacts.ext) : '') +
+ '", mac="' + mac + '"';
+
+ if (artifacts.app) {
+ header += ', app="' + artifacts.app +
+ (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
+ }
+
+ result.field = header;
+
+ return result;
+ },
+
+ // Generate a bewit value for a given URI
+
+ /*
+ uri: 'http://example.com/resource?a=b'
+ options: {
+
+ // Required
+
+ credentials: {
+ id: 'dh37fgj492je',
+ key: 'aoijedoaijsdlaksjdl',
+ algorithm: 'sha256' // 'sha1', 'sha256'
+ },
+ ttlSec: 60 * 60, // TTL in seconds
+
+ // Optional
+
+ ext: 'application-specific', // Application specific data sent via the ext attribute
+ localtimeOffsetMsec: 400 // Time offset to sync with server time
+ };
+ */
+
+ bewit: function (uri, options) {
+
+ // Validate inputs
+
+ if (!uri ||
+ (typeof uri !== 'string') ||
+ !options ||
+ typeof options !== 'object' ||
+ !options.ttlSec) {
+
+ return '';
+ }
+
+ options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value
+
+ // Application time
+
+ var now = hawk.utils.now(options.localtimeOffsetMsec);
+
+ // Validate credentials
+
+ var credentials = options.credentials;
+ if (!credentials ||
+ !credentials.id ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ return '';
+ }
+
+ if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return '';
+ }
+
+ // Parse URI
+
+ uri = hawk.utils.parseUri(uri);
+
+ // Calculate signature
+
+ var exp = now + options.ttlSec;
+ var mac = hawk.crypto.calculateMac('bewit', credentials, {
+ ts: exp,
+ nonce: '',
+ method: 'GET',
+ resource: uri.relative, // Maintain trailing '?' and query params
+ host: uri.hostname,
+ port: uri.port,
+ ext: options.ext
+ });
+
+ // Construct bewit: id\exp\mac\ext
+
+ var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
+ return hawk.utils.base64urlEncode(bewit);
+ },
+
+ // Validate server response
+
+ /*
+ request: object created via 'new XMLHttpRequest()' after response received
+ artifacts: object received from header().artifacts
+ options: {
+ payload: optional payload received
+ required: specifies if a Server-Authorization header is required. Defaults to 'false'
+ }
+ */
+
+ authenticate: function (request, credentials, artifacts, options) {
+
+ options = options || {};
+
+ var getHeader = function (name) {
+
+ return request.getResponseHeader ? request.getResponseHeader(name) : request.getHeader(name);
+ };
+
+ var wwwAuthenticate = getHeader('www-authenticate');
+ if (wwwAuthenticate) {
+
+ // Parse HTTP WWW-Authenticate header
+
+ var attributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']);
+ if (!attributes) {
+ return false;
+ }
+
+ if (attributes.ts) {
+ var tsm = hawk.crypto.calculateTsMac(attributes.ts, credentials);
+ if (tsm !== attributes.tsm) {
+ return false;
+ }
+
+ hawk.utils.setNtpOffset(attributes.ts - Math.floor((new Date()).getTime() / 1000)); // Keep offset at 1 second precision
+ }
+ }
+
+ // Parse HTTP Server-Authorization header
+
+ var serverAuthorization = getHeader('server-authorization');
+ if (!serverAuthorization &&
+ !options.required) {
+
+ return true;
+ }
+
+ var attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']);
+ if (!attributes) {
+ return false;
+ }
+
+ var modArtifacts = {
+ ts: artifacts.ts,
+ nonce: artifacts.nonce,
+ method: artifacts.method,
+ resource: artifacts.resource,
+ host: artifacts.host,
+ port: artifacts.port,
+ hash: attributes.hash,
+ ext: attributes.ext,
+ app: artifacts.app,
+ dlg: artifacts.dlg
+ };
+
+ var mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
+ if (mac !== attributes.mac) {
+ return false;
+ }
+
+ if (!options.payload &&
+ options.payload !== '') {
+
+ return true;
+ }
+
+ if (!attributes.hash) {
+ return false;
+ }
+
+ var calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, getHeader('content-type'));
+ return (calculatedHash === attributes.hash);
+ },
+
+ message: function (host, port, message, options) {
+
+ // Validate inputs
+
+ if (!host || typeof host !== 'string' ||
+ !port || typeof port !== 'number' ||
+ message === null || message === undefined || typeof message !== 'string' ||
+ !options || typeof options !== 'object') {
+
+ return null;
+ }
+
+ // Application time
+
+ var timestamp = options.timestamp || hawk.utils.now(options.localtimeOffsetMsec);
+
+ // Validate credentials
+
+ var credentials = options.credentials;
+ if (!credentials ||
+ !credentials.id ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ // Invalid credential object
+ return null;
+ }
+
+ if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return null;
+ }
+
+ // Calculate signature
+
+ var artifacts = {
+ ts: timestamp,
+ nonce: options.nonce || hawk.utils.randomString(6),
+ host: host,
+ port: port,
+ hash: hawk.crypto.calculatePayloadHash(message, credentials.algorithm)
+ };
+
+ // Construct authorization
+
+ var result = {
+ id: credentials.id,
+ ts: artifacts.ts,
+ nonce: artifacts.nonce,
+ hash: artifacts.hash,
+ mac: hawk.crypto.calculateMac('message', credentials, artifacts)
+ };
+
+ return result;
+ },
+
+ authenticateTimestamp: function (message, credentials, updateClock) { // updateClock defaults to true
+
+ var tsm = hawk.crypto.calculateTsMac(message.ts, credentials);
+ if (tsm !== message.tsm) {
+ return false;
+ }
+
+ if (updateClock !== false) {
+ hawk.utils.setNtpOffset(message.ts - Math.floor((new Date()).getTime() / 1000)); // Keep offset at 1 second precision
+ }
+
+ return true;
+ }
+};
+
+
+hawk.crypto = {
+
+ headerVersion: '1',
+
+ algorithms: ['sha1', 'sha256'],
+
+ calculateMac: function (type, credentials, options) {
+
+ var normalized = hawk.crypto.generateNormalizedString(type, options);
+
+ var hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
+ return hmac.toString(CryptoJS.enc.Base64);
+ },
+
+ generateNormalizedString: function (type, options) {
+
+ var normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' +
+ options.ts + '\n' +
+ options.nonce + '\n' +
+ (options.method || '').toUpperCase() + '\n' +
+ (options.resource || '') + '\n' +
+ options.host.toLowerCase() + '\n' +
+ options.port + '\n' +
+ (options.hash || '') + '\n';
+
+ if (options.ext) {
+ normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');
+ }
+
+ normalized += '\n';
+
+ if (options.app) {
+ normalized += options.app + '\n' +
+ (options.dlg || '') + '\n';
+ }
+
+ return normalized;
+ },
+
+ calculatePayloadHash: function (payload, algorithm, contentType) {
+
+ var hash = CryptoJS.algo[algorithm.toUpperCase()].create();
+ hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');
+ hash.update(hawk.utils.parseContentType(contentType) + '\n');
+ hash.update(payload);
+ hash.update('\n');
+ return hash.finalize().toString(CryptoJS.enc.Base64);
+ },
+
+ calculateTsMac: function (ts, credentials) {
+
+ var hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
+ return hash.toString(CryptoJS.enc.Base64);
+ }
+};
+
+
+// localStorage compatible interface
+
+hawk.internals.LocalStorage = function () {
+
+ this._cache = {};
+ this.length = 0;
+
+ this.getItem = function (key) {
+
+ return this._cache.hasOwnProperty(key) ? String(this._cache[key]) : null;
+ };
+
+ this.setItem = function (key, value) {
+
+ this._cache[key] = String(value);
+ this.length = Object.keys(this._cache).length;
+ };
+
+ this.removeItem = function (key) {
+
+ delete this._cache[key];
+ this.length = Object.keys(this._cache).length;
+ };
+
+ this.clear = function () {
+
+ this._cache = {};
+ this.length = 0;
+ };
+
+ this.key = function (i) {
+
+ return Object.keys(this._cache)[i || 0];
+ };
+};
+
+
+hawk.utils = {
+
+ storage: new hawk.internals.LocalStorage(),
+
+ setStorage: function (storage) {
+
+ var ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset');
+ hawk.utils.storage = storage;
+ if (ntpOffset) {
+ hawk.utils.setNtpOffset(ntpOffset);
+ }
+ },
+
+ setNtpOffset: function (offset) {
+
+ try {
+ hawk.utils.storage.setItem('hawk_ntp_offset', offset);
+ }
+ catch (err) {
+ console.error('[hawk] could not write to storage.');
+ console.error(err);
+ }
+ },
+
+ getNtpOffset: function () {
+
+ var offset = hawk.utils.storage.getItem('hawk_ntp_offset');
+ if (!offset) {
+ return 0;
+ }
+
+ return parseInt(offset, 10);
+ },
+
+ now: function (localtimeOffsetMsec) {
+
+ return Math.floor(((new Date()).getTime() + (localtimeOffsetMsec || 0)) / 1000) + hawk.utils.getNtpOffset();
+ },
+
+ escapeHeaderAttribute: function (attribute) {
+
+ return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');
+ },
+
+ parseContentType: function (header) {
+
+ if (!header) {
+ return '';
+ }
+
+ return header.split(';')[0].replace(/^\s+|\s+$/g, '').toLowerCase();
+ },
+
+ parseAuthorizationHeader: function (header, keys) {
+
+ if (!header) {
+ return null;
+ }
+
+ var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
+ if (!headerParts) {
+ return null;
+ }
+
+ var scheme = headerParts[1];
+ if (scheme.toLowerCase() !== 'hawk') {
+ return null;
+ }
+
+ var attributesString = headerParts[2];
+ if (!attributesString) {
+ return null;
+ }
+
+ var attributes = {};
+ var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
+
+ // Check valid attribute names
+
+ if (keys.indexOf($1) === -1) {
+ return;
+ }
+
+ // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
+
+ if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
+ return;
+ }
+
+ // Check for duplicates
+
+ if (attributes.hasOwnProperty($1)) {
+ return;
+ }
+
+ attributes[$1] = $2;
+ return '';
+ });
+
+ if (verify !== '') {
+ return null;
+ }
+
+ return attributes;
+ },
+
+ randomString: function (size) {
+
+ var randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+ var len = randomSource.length;
+
+ var result = [];
+ for (var i = 0; i < size; ++i) {
+ result[i] = randomSource[Math.floor(Math.random() * len)];
+ }
+
+ return result.join('');
+ },
+
+ parseUri: function (input) {
+
+ // Based on: parseURI 1.2.2
+ // http://blog.stevenlevithan.com/archives/parseuri
+ // (c) Steven Levithan <stevenlevithan.com>
+ // MIT License
+
+ var keys = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'hostname', 'port', 'resource', 'relative', 'pathname', 'directory', 'file', 'query', 'fragment'];
+
+ var uriRegex = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?)(?:#(.*))?)/;
+ var uriByNumber = input.match(uriRegex);
+ var uri = {};
+
+ for (var i = 0, il = keys.length; i < il; ++i) {
+ uri[keys[i]] = uriByNumber[i] || '';
+ }
+
+ if (uri.port === '') {
+ uri.port = (uri.protocol.toLowerCase() === 'http' ? '80' : (uri.protocol.toLowerCase() === 'https' ? '443' : ''));
+ }
+
+ return uri;
+ },
+
+ base64urlEncode: function (value) {
+
+ var wordArray = CryptoJS.enc.Utf8.parse(value);
+ var encoded = CryptoJS.enc.Base64.stringify(wordArray);
+ return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
+ }
+};
+
+
+// $lab:coverage:off$
+
+// Based on: Crypto-JS v3.1.2
+// Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
+// http://code.google.com/p/crypto-js/
+// http://code.google.com/p/crypto-js/wiki/License
+
+var CryptoJS = CryptoJS || function (h, r) { var k = {}, l = k.lib = {}, n = function () { }, f = l.Base = { extend: function (a) { n.prototype = this; var b = new n; a && b.mixIn(a); b.hasOwnProperty("init") || (b.init = function () { b.$super.init.apply(this, arguments) }); b.init.prototype = b; b.$super = this; return b }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () { }, mixIn: function (a) { for (var b in a) a.hasOwnProperty(b) && (this[b] = a[b]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } }, j = l.WordArray = f.extend({ init: function (a, b) { a = this.words = a || []; this.sigBytes = b != r ? b : 4 * a.length }, toString: function (a) { return (a || s).stringify(this) }, concat: function (a) { var b = this.words, d = a.words, c = this.sigBytes; a = a.sigBytes; this.clamp(); if (c % 4) for (var e = 0; e < a; e++) b[c + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 - 8 * ((c + e) % 4); else if (65535 < d.length) for (e = 0; e < a; e += 4) b[c + e >>> 2] = d[e >>> 2]; else b.push.apply(b, d); this.sigBytes += a; return this }, clamp: function () { var a = this.words, b = this.sigBytes; a[b >>> 2] &= 4294967295 << 32 - 8 * (b % 4); a.length = h.ceil(b / 4) }, clone: function () { var a = f.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (var b = [], d = 0; d < a; d += 4) b.push(4294967296 * h.random() | 0); return new j.init(b, a) } }), m = k.enc = {}, s = m.Hex = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) { var e = b[c >>> 2] >>> 24 - 8 * (c % 4) & 255; d.push((e >>> 4).toString(16)); d.push((e & 15).toString(16)) } return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c += 2) d[c >>> 3] |= parseInt(a.substr(c, 2), 16) << 24 - 4 * (c % 8); return new j.init(d, b / 2) } }, p = m.Latin1 = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) d.push(String.fromCharCode(b[c >>> 2] >>> 24 - 8 * (c % 4) & 255)); return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c++) d[c >>> 2] |= (a.charCodeAt(c) & 255) << 24 - 8 * (c % 4); return new j.init(d, b) } }, t = m.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(p.stringify(a))) } catch (b) { throw Error("Malformed UTF-8 data"); } }, parse: function (a) { return p.parse(unescape(encodeURIComponent(a))) } }, q = l.BufferedBlockAlgorithm = f.extend({ reset: function () { this._data = new j.init; this._nDataBytes = 0 }, _append: function (a) { "string" == typeof a && (a = t.parse(a)); this._data.concat(a); this._nDataBytes += a.sigBytes }, _process: function (a) { var b = this._data, d = b.words, c = b.sigBytes, e = this.blockSize, f = c / (4 * e), f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); a = f * e; c = h.min(4 * a, c); if (a) { for (var g = 0; g < a; g += e) this._doProcessBlock(d, g); g = d.splice(0, a); b.sigBytes -= c } return new j.init(g, c) }, clone: function () { var a = f.clone.call(this); a._data = this._data.clone(); return a }, _minBufferSize: 0 }); l.Hasher = q.extend({ cfg: f.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { q.reset.call(this); this._doReset() }, update: function (a) { this._append(a); this._process(); return this }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, blockSize: 16, _createHelper: function (a) { return function (b, d) { return (new a.init(d)).finalize(b) } }, _createHmacHelper: function (a) { return function (b, d) { return (new u.HMAC.init(a, d)).finalize(b) } } }); var u = k.algo = {}; return k }(Math);
+(function () { var k = CryptoJS, b = k.lib, m = b.WordArray, l = b.Hasher, d = [], b = k.algo.SHA1 = l.extend({ _doReset: function () { this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (n, p) { for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) { if (16 > c) d[c] = n[p + c] | 0; else { var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16]; d[c] = g << 1 | g >>> 31 } g = (e << 5 | e >>> 27) + b + d[c]; g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514); b = j; j = h; h = f << 30 | f >>> 2; f = e; e = g } a[0] = a[0] + e | 0; a[1] = a[1] + f | 0; a[2] = a[2] + h | 0; a[3] = a[3] + j | 0; a[4] = a[4] + b | 0 }, _doFinalize: function () { var b = this._data, d = b.words, a = 8 * this._nDataBytes, e = 8 * b.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296); d[(e + 64 >>> 9 << 4) + 15] = a; b.sigBytes = 4 * d.length; this._process(); return this._hash }, clone: function () { var b = l.clone.call(this); b._hash = this._hash.clone(); return b } }); k.SHA1 = l._createHelper(b); k.HmacSHA1 = l._createHmacHelper(b) })();
+(function (k) { for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function (q) { return 4294967296 * (q - (q | 0)) | 0 }, l = 2, b = 0; 64 > b;) { var d; a: { d = l; for (var w = k.sqrt(d), r = 2; r <= w; r++) if (!(d % r)) { d = !1; break a } d = !0 } d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++); l++ } var n = [], h = h.SHA256 = j.extend({ _doReset: function () { this._hash = new v.init(s.slice(0)) }, _doProcessBlock: function (q, h) { for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) { if (16 > e) n[e] = q[h + e] | 0; else { var m = n[e - 15], p = n[e - 2]; n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16] } m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e]; p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b); l = j; j = g; g = f; f = k + m | 0; k = b; b = d; d = c; c = m + p | 0 } a[0] = a[0] + c | 0; a[1] = a[1] + d | 0; a[2] = a[2] + b | 0; a[3] = a[3] + k | 0; a[4] = a[4] + f | 0; a[5] = a[5] + g | 0; a[6] = a[6] + j | 0; a[7] = a[7] + l | 0 }, _doFinalize: function () { var d = this._data, b = d.words, a = 8 * this._nDataBytes, c = 8 * d.sigBytes; b[c >>> 5] |= 128 << 24 - c % 32; b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296); b[(c + 64 >>> 9 << 4) + 15] = a; d.sigBytes = 4 * b.length; this._process(); return this._hash }, clone: function () { var b = j.clone.call(this); b._hash = this._hash.clone(); return b } }); g.SHA256 = j._createHelper(h); g.HmacSHA256 = j._createHmacHelper(h) })(Math);
+(function () { var c = CryptoJS, k = c.enc.Utf8; c.algo.HMAC = c.lib.Base.extend({ init: function (a, b) { a = this._hasher = new a.init; "string" == typeof b && (b = k.parse(b)); var c = a.blockSize, e = 4 * c; b.sigBytes > e && (b = a.finalize(b)); b.clamp(); for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++) h[d] ^= 1549556828, j[d] ^= 909522486; f.sigBytes = g.sigBytes = e; this.reset() }, reset: function () { var a = this._hasher; a.reset(); a.update(this._iKey) }, update: function (a) { this._hasher.update(a); return this }, finalize: function (a) { var b = this._hasher; a = b.finalize(a); b.reset(); return b.finalize(this._oKey.clone().concat(a)) } }) })();
+(function () { var h = CryptoJS, j = h.lib.WordArray; h.enc.Base64 = { stringify: function (b) { var e = b.words, f = b.sigBytes, c = this._map; b.clamp(); b = []; for (var a = 0; a < f; a += 3) for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) b.push(c.charAt(d >>> 6 * (3 - g) & 63)); if (e = c.charAt(64)) for (; b.length % 4;) b.push(e); return b.join("") }, parse: function (b) { var e = b.length, f = this._map, c = f.charAt(64); c && (c = b.indexOf(c), -1 != c && (e = c)); for (var c = [], a = 0, d = 0; d < e; d++) if (d % 4) { var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4), h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4); c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4); a++ } return j.create(c, a) }, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" } })();
+
+hawk.crypto.internals = CryptoJS;
+
+
+// Export if used as a module
+
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = hawk;
+}
+
+// $lab:coverage:on$
--- /dev/null
+// Load modules
+
+var Url = require('url');
+var Hoek = require('hoek');
+var Cryptiles = require('cryptiles');
+var Crypto = require('./crypto');
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Generate an Authorization header for a given request
+
+/*
+ uri: 'http://example.com/resource?a=b' or object from Url.parse()
+ method: HTTP verb (e.g. 'GET', 'POST')
+ options: {
+
+ // Required
+
+ credentials: {
+ id: 'dh37fgj492je',
+ key: 'aoijedoaijsdlaksjdl',
+ algorithm: 'sha256' // 'sha1', 'sha256'
+ },
+
+ // Optional
+
+ ext: 'application-specific', // Application specific data sent via the ext attribute
+ timestamp: Date.now(), // A pre-calculated timestamp
+ nonce: '2334f34f', // A pre-generated nonce
+ localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
+ payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
+ contentType: 'application/json', // Payload content-type (ignored if hash provided)
+ hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
+ app: '24s23423f34dx', // Oz application id
+ dlg: '234sz34tww3sd' // Oz delegated-by application id
+ }
+*/
+
+exports.header = function (uri, method, options) {
+
+ var result = {
+ field: '',
+ artifacts: {}
+ };
+
+ // Validate inputs
+
+ if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
+ !method || typeof method !== 'string' ||
+ !options || typeof options !== 'object') {
+
+ result.err = 'Invalid argument type';
+ return result;
+ }
+
+ // Application time
+
+ var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
+
+ // Validate credentials
+
+ var credentials = options.credentials;
+ if (!credentials ||
+ !credentials.id ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ result.err = 'Invalid credential object';
+ return result;
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ result.err = 'Unknown algorithm';
+ return result;
+ }
+
+ // Parse URI
+
+ if (typeof uri === 'string') {
+ uri = Url.parse(uri);
+ }
+
+ // Calculate signature
+
+ var artifacts = {
+ ts: timestamp,
+ nonce: options.nonce || Cryptiles.randomString(6),
+ method: method,
+ resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
+ host: uri.hostname,
+ port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
+ hash: options.hash,
+ ext: options.ext,
+ app: options.app,
+ dlg: options.dlg
+ };
+
+ result.artifacts = artifacts;
+
+ // Calculate payload hash
+
+ if (!artifacts.hash &&
+ (options.payload || options.payload === '')) {
+
+ artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
+ }
+
+ var mac = Crypto.calculateMac('header', credentials, artifacts);
+
+ // Construct header
+
+ var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
+ var header = 'Hawk id="' + credentials.id +
+ '", ts="' + artifacts.ts +
+ '", nonce="' + artifacts.nonce +
+ (artifacts.hash ? '", hash="' + artifacts.hash : '') +
+ (hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') +
+ '", mac="' + mac + '"';
+
+ if (artifacts.app) {
+ header += ', app="' + artifacts.app +
+ (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
+ }
+
+ result.field = header;
+
+ return result;
+};
+
+
+// Validate server response
+
+/*
+ res: node's response object
+ artifacts: object received from header().artifacts
+ options: {
+ payload: optional payload received
+ required: specifies if a Server-Authorization header is required. Defaults to 'false'
+ }
+*/
+
+exports.authenticate = function (res, credentials, artifacts, options) {
+
+ artifacts = Hoek.clone(artifacts);
+ options = options || {};
+
+ if (res.headers['www-authenticate']) {
+
+ // Parse HTTP WWW-Authenticate header
+
+ var attributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
+ if (attributes instanceof Error) {
+ return false;
+ }
+
+ // Validate server timestamp (not used to update clock since it is done via the SNPT client)
+
+ if (attributes.ts) {
+ var tsm = Crypto.calculateTsMac(attributes.ts, credentials);
+ if (tsm !== attributes.tsm) {
+ return false;
+ }
+ }
+ }
+
+ // Parse HTTP Server-Authorization header
+
+ if (!res.headers['server-authorization'] &&
+ !options.required) {
+
+ return true;
+ }
+
+ var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
+ if (attributes instanceof Error) {
+ return false;
+ }
+
+ artifacts.ext = attributes.ext;
+ artifacts.hash = attributes.hash;
+
+ var mac = Crypto.calculateMac('response', credentials, artifacts);
+ if (mac !== attributes.mac) {
+ return false;
+ }
+
+ if (!options.payload &&
+ options.payload !== '') {
+
+ return true;
+ }
+
+ if (!attributes.hash) {
+ return false;
+ }
+
+ var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
+ return (calculatedHash === attributes.hash);
+};
+
+
+// Generate a bewit value for a given URI
+
+/*
+ uri: 'http://example.com/resource?a=b' or object from Url.parse()
+ options: {
+
+ // Required
+
+ credentials: {
+ id: 'dh37fgj492je',
+ key: 'aoijedoaijsdlaksjdl',
+ algorithm: 'sha256' // 'sha1', 'sha256'
+ },
+ ttlSec: 60 * 60, // TTL in seconds
+
+ // Optional
+
+ ext: 'application-specific', // Application specific data sent via the ext attribute
+ localtimeOffsetMsec: 400 // Time offset to sync with server time
+ };
+*/
+
+exports.getBewit = function (uri, options) {
+
+ // Validate inputs
+
+ if (!uri ||
+ (typeof uri !== 'string' && typeof uri !== 'object') ||
+ !options ||
+ typeof options !== 'object' ||
+ !options.ttlSec) {
+
+ return '';
+ }
+
+ options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value
+
+ // Application time
+
+ var now = Utils.now(options.localtimeOffsetMsec);
+
+ // Validate credentials
+
+ var credentials = options.credentials;
+ if (!credentials ||
+ !credentials.id ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ return '';
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return '';
+ }
+
+ // Parse URI
+
+ if (typeof uri === 'string') {
+ uri = Url.parse(uri);
+ }
+
+ // Calculate signature
+
+ var exp = Math.floor(now / 1000) + options.ttlSec;
+ var mac = Crypto.calculateMac('bewit', credentials, {
+ ts: exp,
+ nonce: '',
+ method: 'GET',
+ resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
+ host: uri.hostname,
+ port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
+ ext: options.ext
+ });
+
+ // Construct bewit: id\exp\mac\ext
+
+ var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
+ return Hoek.base64urlEncode(bewit);
+};
+
+
+// Generate an authorization string for a message
+
+/*
+ host: 'example.com',
+ port: 8000,
+ message: '{"some":"payload"}', // UTF-8 encoded string for body hash generation
+ options: {
+
+ // Required
+
+ credentials: {
+ id: 'dh37fgj492je',
+ key: 'aoijedoaijsdlaksjdl',
+ algorithm: 'sha256' // 'sha1', 'sha256'
+ },
+
+ // Optional
+
+ timestamp: Date.now(), // A pre-calculated timestamp
+ nonce: '2334f34f', // A pre-generated nonce
+ localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
+ }
+*/
+
+exports.message = function (host, port, message, options) {
+
+ // Validate inputs
+
+ if (!host || typeof host !== 'string' ||
+ !port || typeof port !== 'number' ||
+ message === null || message === undefined || typeof message !== 'string' ||
+ !options || typeof options !== 'object') {
+
+ return null;
+ }
+
+ // Application time
+
+ var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
+
+ // Validate credentials
+
+ var credentials = options.credentials;
+ if (!credentials ||
+ !credentials.id ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ // Invalid credential object
+ return null;
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return null;
+ }
+
+ // Calculate signature
+
+ var artifacts = {
+ ts: timestamp,
+ nonce: options.nonce || Cryptiles.randomString(6),
+ host: host,
+ port: port,
+ hash: Crypto.calculatePayloadHash(message, credentials.algorithm)
+ };
+
+ // Construct authorization
+
+ var result = {
+ id: credentials.id,
+ ts: artifacts.ts,
+ nonce: artifacts.nonce,
+ hash: artifacts.hash,
+ mac: Crypto.calculateMac('message', credentials, artifacts)
+ };
+
+ return result;
+};
+
+
+
--- /dev/null
+// Load modules
+
+var Crypto = require('crypto');
+var Url = require('url');
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// MAC normalization format version
+
+exports.headerVersion = '1'; // Prevent comparison of mac values generated with different normalized string formats
+
+
+// Supported HMAC algorithms
+
+exports.algorithms = ['sha1', 'sha256'];
+
+
+// Calculate the request MAC
+
+/*
+ type: 'header', // 'header', 'bewit', 'response'
+ credentials: {
+ key: 'aoijedoaijsdlaksjdl',
+ algorithm: 'sha256' // 'sha1', 'sha256'
+ },
+ options: {
+ method: 'GET',
+ resource: '/resource?a=1&b=2',
+ host: 'example.com',
+ port: 8080,
+ ts: 1357718381034,
+ nonce: 'd3d345f',
+ hash: 'U4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=',
+ ext: 'app-specific-data',
+ app: 'hf48hd83qwkj', // Application id (Oz)
+ dlg: 'd8djwekds9cj' // Delegated by application id (Oz), requires options.app
+ }
+*/
+
+exports.calculateMac = function (type, credentials, options) {
+
+ var normalized = exports.generateNormalizedString(type, options);
+
+ var hmac = Crypto.createHmac(credentials.algorithm, credentials.key).update(normalized);
+ var digest = hmac.digest('base64');
+ return digest;
+};
+
+
+exports.generateNormalizedString = function (type, options) {
+
+ var resource = options.resource || '';
+ if (resource &&
+ resource[0] !== '/') {
+
+ var url = Url.parse(resource, false);
+ resource = url.path; // Includes query
+ }
+
+ var normalized = 'hawk.' + exports.headerVersion + '.' + type + '\n' +
+ options.ts + '\n' +
+ options.nonce + '\n' +
+ (options.method || '').toUpperCase() + '\n' +
+ resource + '\n' +
+ options.host.toLowerCase() + '\n' +
+ options.port + '\n' +
+ (options.hash || '') + '\n';
+
+ if (options.ext) {
+ normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');
+ }
+
+ normalized += '\n';
+
+ if (options.app) {
+ normalized += options.app + '\n' +
+ (options.dlg || '') + '\n';
+ }
+
+ return normalized;
+};
+
+
+exports.calculatePayloadHash = function (payload, algorithm, contentType) {
+
+ var hash = exports.initializePayloadHash(algorithm, contentType);
+ hash.update(payload || '');
+ return exports.finalizePayloadHash(hash);
+};
+
+
+exports.initializePayloadHash = function (algorithm, contentType) {
+
+ var hash = Crypto.createHash(algorithm);
+ hash.update('hawk.' + exports.headerVersion + '.payload\n');
+ hash.update(Utils.parseContentType(contentType) + '\n');
+ return hash;
+};
+
+
+exports.finalizePayloadHash = function (hash) {
+
+ hash.update('\n');
+ return hash.digest('base64');
+};
+
+
+exports.calculateTsMac = function (ts, credentials) {
+
+ var hmac = Crypto.createHmac(credentials.algorithm, credentials.key);
+ hmac.update('hawk.' + exports.headerVersion + '.ts\n' + ts + '\n');
+ return hmac.digest('base64');
+};
+
+
+exports.timestampMessage = function (credentials, localtimeOffsetMsec) {
+
+ var now = Utils.nowSecs(localtimeOffsetMsec);
+ var tsm = exports.calculateTsMac(now, credentials);
+ return { ts: now, tsm: tsm };
+};
--- /dev/null
+// Export sub-modules
+
+exports.error = exports.Error = require('boom');
+exports.sntp = require('sntp');
+
+exports.server = require('./server');
+exports.client = require('./client');
+exports.crypto = require('./crypto');
+exports.utils = require('./utils');
+
+exports.uri = {
+ authenticate: exports.server.authenticateBewit,
+ getBewit: exports.client.getBewit
+};
+
--- /dev/null
+// Load modules
+
+var Boom = require('boom');
+var Hoek = require('hoek');
+var Cryptiles = require('cryptiles');
+var Crypto = require('./crypto');
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Hawk authentication
+
+/*
+ req: node's HTTP request object or an object as follows:
+
+ var request = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="'
+ };
+
+ credentialsFunc: required function to lookup the set of Hawk credentials based on the provided credentials id.
+ The credentials include the MAC key, MAC algorithm, and other attributes (such as username)
+ needed by the application. This function is the equivalent of verifying the username and
+ password in Basic authentication.
+
+ var credentialsFunc = function (id, callback) {
+
+ // Lookup credentials in database
+ db.lookup(id, function (err, item) {
+
+ if (err || !item) {
+ return callback(err);
+ }
+
+ var credentials = {
+ // Required
+ key: item.key,
+ algorithm: item.algorithm,
+ // Application specific
+ user: item.user
+ };
+
+ return callback(null, credentials);
+ });
+ };
+
+ options: {
+
+ hostHeaderName: optional header field name, used to override the default 'Host' header when used
+ behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving
+ the original (which is what the module must verify) in the 'x-forwarded-host' header field.
+ Only used when passed a node Http.ServerRequest object.
+
+ nonceFunc: optional nonce validation function. The function signature is function(nonce, ts, callback)
+ where 'callback' must be called using the signature function(err).
+
+ timestampSkewSec: optional number of seconds of permitted clock skew for incoming timestamps. Defaults to 60 seconds.
+ Provides a +/- skew which means actual allowed window is double the number of seconds.
+
+ localtimeOffsetMsec: optional local clock time offset express in a number of milliseconds (positive or negative).
+ Defaults to 0.
+
+ payload: optional payload for validation. The client calculates the hash value and includes it via the 'hash'
+ header attribute. The server always ensures the value provided has been included in the request
+ MAC. When this option is provided, it validates the hash value itself. Validation is done by calculating
+ a hash value over the entire payload (assuming it has already be normalized to the same format and
+ encoding used by the client to calculate the hash on request). If the payload is not available at the time
+ of authentication, the authenticatePayload() method can be used by passing it the credentials and
+ attributes.hash returned in the authenticate callback.
+
+ host: optional host name override. Only used when passed a node request object.
+ port: optional port override. Only used when passed a node request object.
+ }
+
+ callback: function (err, credentials, artifacts) { }
+ */
+
+exports.authenticate = function (req, credentialsFunc, options, callback) {
+
+ callback = Hoek.nextTick(callback);
+
+ // Default options
+
+ options.nonceFunc = options.nonceFunc || function (nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation
+ options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
+
+ // Application time
+
+ var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
+
+ // Convert node Http request object to a request configuration object
+
+ var request = Utils.parseRequest(req, options);
+ if (request instanceof Error) {
+ return callback(Boom.badRequest(request.message));
+ }
+
+ // Parse HTTP Authorization header
+
+ var attributes = Utils.parseAuthorizationHeader(request.authorization);
+ if (attributes instanceof Error) {
+ return callback(attributes);
+ }
+
+ // Construct artifacts container
+
+ var artifacts = {
+ method: request.method,
+ host: request.host,
+ port: request.port,
+ resource: request.url,
+ ts: attributes.ts,
+ nonce: attributes.nonce,
+ hash: attributes.hash,
+ ext: attributes.ext,
+ app: attributes.app,
+ dlg: attributes.dlg,
+ mac: attributes.mac,
+ id: attributes.id
+ };
+
+ // Verify required header attributes
+
+ if (!attributes.id ||
+ !attributes.ts ||
+ !attributes.nonce ||
+ !attributes.mac) {
+
+ return callback(Boom.badRequest('Missing attributes'), null, artifacts);
+ }
+
+ // Fetch Hawk credentials
+
+ credentialsFunc(attributes.id, function (err, credentials) {
+
+ if (err) {
+ return callback(err, credentials || null, artifacts);
+ }
+
+ if (!credentials) {
+ return callback(Boom.unauthorized('Unknown credentials', 'Hawk'), null, artifacts);
+ }
+
+ if (!credentials.key ||
+ !credentials.algorithm) {
+
+ return callback(Boom.internal('Invalid credentials'), credentials, artifacts);
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return callback(Boom.internal('Unknown algorithm'), credentials, artifacts);
+ }
+
+ // Calculate MAC
+
+ var mac = Crypto.calculateMac('header', credentials, artifacts);
+ if (!Cryptiles.fixedTimeComparison(mac, attributes.mac)) {
+ return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials, artifacts);
+ }
+
+ // Check payload hash
+
+ if (options.payload ||
+ options.payload === '') {
+
+ if (!attributes.hash) {
+ return callback(Boom.unauthorized('Missing required payload hash', 'Hawk'), credentials, artifacts);
+ }
+
+ var hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);
+ if (!Cryptiles.fixedTimeComparison(hash, attributes.hash)) {
+ return callback(Boom.unauthorized('Bad payload hash', 'Hawk'), credentials, artifacts);
+ }
+ }
+
+ // Check nonce
+
+ options.nonceFunc(attributes.nonce, attributes.ts, function (err) {
+
+ if (err) {
+ return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials, artifacts);
+ }
+
+ // Check timestamp staleness
+
+ if (Math.abs((attributes.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
+ var tsm = Crypto.timestampMessage(credentials, options.localtimeOffsetMsec);
+ return callback(Boom.unauthorized('Stale timestamp', 'Hawk', tsm), credentials, artifacts);
+ }
+
+ // Successful authentication
+
+ return callback(null, credentials, artifacts);
+ });
+ });
+};
+
+
+// Authenticate payload hash - used when payload cannot be provided during authenticate()
+
+/*
+ payload: raw request payload
+ credentials: from authenticate callback
+ artifacts: from authenticate callback
+ contentType: req.headers['content-type']
+*/
+
+exports.authenticatePayload = function (payload, credentials, artifacts, contentType) {
+
+ var calculatedHash = Crypto.calculatePayloadHash(payload, credentials.algorithm, contentType);
+ return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
+};
+
+
+// Authenticate payload hash - used when payload cannot be provided during authenticate()
+
+/*
+ calculatedHash: the payload hash calculated using Crypto.calculatePayloadHash()
+ artifacts: from authenticate callback
+*/
+
+exports.authenticatePayloadHash = function (calculatedHash, artifacts) {
+
+ return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
+};
+
+
+// Generate a Server-Authorization header for a given response
+
+/*
+ credentials: {}, // Object received from authenticate()
+ artifacts: {} // Object received from authenticate(); 'mac', 'hash', and 'ext' - ignored
+ options: {
+ ext: 'application-specific', // Application specific data sent via the ext attribute
+ payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
+ contentType: 'application/json', // Payload content-type (ignored if hash provided)
+ hash: 'U4MKKSmiVxk37JCCrAVIjV=' // Pre-calculated payload hash
+ }
+*/
+
+exports.header = function (credentials, artifacts, options) {
+
+ // Prepare inputs
+
+ options = options || {};
+
+ if (!artifacts ||
+ typeof artifacts !== 'object' ||
+ typeof options !== 'object') {
+
+ return '';
+ }
+
+ artifacts = Hoek.clone(artifacts);
+ delete artifacts.mac;
+ artifacts.hash = options.hash;
+ artifacts.ext = options.ext;
+
+ // Validate credentials
+
+ if (!credentials ||
+ !credentials.key ||
+ !credentials.algorithm) {
+
+ // Invalid credential object
+ return '';
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return '';
+ }
+
+ // Calculate payload hash
+
+ if (!artifacts.hash &&
+ (options.payload || options.payload === '')) {
+
+ artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
+ }
+
+ var mac = Crypto.calculateMac('response', credentials, artifacts);
+
+ // Construct header
+
+ var header = 'Hawk mac="' + mac + '"' +
+ (artifacts.hash ? ', hash="' + artifacts.hash + '"' : '');
+
+ if (artifacts.ext !== null &&
+ artifacts.ext !== undefined &&
+ artifacts.ext !== '') { // Other falsey values allowed
+
+ header += ', ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) + '"';
+ }
+
+ return header;
+};
+
+
+/*
+ * Arguments and options are the same as authenticate() with the exception that the only supported options are:
+ * 'hostHeaderName', 'localtimeOffsetMsec', 'host', 'port'
+ */
+
+exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
+
+ callback = Hoek.nextTick(callback);
+
+ // Application time
+
+ var now = Utils.now(options.localtimeOffsetMsec);
+
+ // Convert node Http request object to a request configuration object
+
+ var request = Utils.parseRequest(req, options);
+ if (request instanceof Error) {
+ return callback(Boom.badRequest(request.message));
+ }
+
+ // Extract bewit
+
+ // 1 2 3 4
+ var resource = request.url.match(/^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/);
+ if (!resource) {
+ return callback(Boom.unauthorized(null, 'Hawk'));
+ }
+
+ // Bewit not empty
+
+ if (!resource[3]) {
+ return callback(Boom.unauthorized('Empty bewit', 'Hawk'));
+ }
+
+ // Verify method is GET
+
+ if (request.method !== 'GET' &&
+ request.method !== 'HEAD') {
+
+ return callback(Boom.unauthorized('Invalid method', 'Hawk'));
+ }
+
+ // No other authentication
+
+ if (request.authorization) {
+ return callback(Boom.badRequest('Multiple authentications'));
+ }
+
+ // Parse bewit
+
+ var bewitString = Hoek.base64urlDecode(resource[3]);
+ if (bewitString instanceof Error) {
+ return callback(Boom.badRequest('Invalid bewit encoding'));
+ }
+
+ // Bewit format: id\exp\mac\ext ('\' is used because it is a reserved header attribute character)
+
+ var bewitParts = bewitString.split('\\');
+ if (bewitParts.length !== 4) {
+ return callback(Boom.badRequest('Invalid bewit structure'));
+ }
+
+ var bewit = {
+ id: bewitParts[0],
+ exp: parseInt(bewitParts[1], 10),
+ mac: bewitParts[2],
+ ext: bewitParts[3] || ''
+ };
+
+ if (!bewit.id ||
+ !bewit.exp ||
+ !bewit.mac) {
+
+ return callback(Boom.badRequest('Missing bewit attributes'));
+ }
+
+ // Construct URL without bewit
+
+ var url = resource[1];
+ if (resource[4]) {
+ url += resource[2] + resource[4];
+ }
+
+ // Check expiration
+
+ if (bewit.exp * 1000 <= now) {
+ return callback(Boom.unauthorized('Access expired', 'Hawk'), null, bewit);
+ }
+
+ // Fetch Hawk credentials
+
+ credentialsFunc(bewit.id, function (err, credentials) {
+
+ if (err) {
+ return callback(err, credentials || null, bewit.ext);
+ }
+
+ if (!credentials) {
+ return callback(Boom.unauthorized('Unknown credentials', 'Hawk'), null, bewit);
+ }
+
+ if (!credentials.key ||
+ !credentials.algorithm) {
+
+ return callback(Boom.internal('Invalid credentials'), credentials, bewit);
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return callback(Boom.internal('Unknown algorithm'), credentials, bewit);
+ }
+
+ // Calculate MAC
+
+ var mac = Crypto.calculateMac('bewit', credentials, {
+ ts: bewit.exp,
+ nonce: '',
+ method: 'GET',
+ resource: url,
+ host: request.host,
+ port: request.port,
+ ext: bewit.ext
+ });
+
+ if (!Cryptiles.fixedTimeComparison(mac, bewit.mac)) {
+ return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials, bewit);
+ }
+
+ // Successful authentication
+
+ return callback(null, credentials, bewit);
+ });
+};
+
+
+/*
+ * options are the same as authenticate() with the exception that the only supported options are:
+ * 'nonceFunc', 'timestampSkewSec', 'localtimeOffsetMsec'
+ */
+
+exports.authenticateMessage = function (host, port, message, authorization, credentialsFunc, options, callback) {
+
+ callback = Hoek.nextTick(callback);
+
+ // Default options
+
+ options.nonceFunc = options.nonceFunc || function (nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation
+ options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
+
+ // Application time
+
+ var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
+
+ // Validate authorization
+
+ if (!authorization.id ||
+ !authorization.ts ||
+ !authorization.nonce ||
+ !authorization.hash ||
+ !authorization.mac) {
+
+ return callback(Boom.badRequest('Invalid authorization'))
+ }
+
+ // Fetch Hawk credentials
+
+ credentialsFunc(authorization.id, function (err, credentials) {
+
+ if (err) {
+ return callback(err, credentials || null);
+ }
+
+ if (!credentials) {
+ return callback(Boom.unauthorized('Unknown credentials', 'Hawk'));
+ }
+
+ if (!credentials.key ||
+ !credentials.algorithm) {
+
+ return callback(Boom.internal('Invalid credentials'), credentials);
+ }
+
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
+ return callback(Boom.internal('Unknown algorithm'), credentials);
+ }
+
+ // Construct artifacts container
+
+ var artifacts = {
+ ts: authorization.ts,
+ nonce: authorization.nonce,
+ host: host,
+ port: port,
+ hash: authorization.hash
+ };
+
+ // Calculate MAC
+
+ var mac = Crypto.calculateMac('message', credentials, artifacts);
+ if (!Cryptiles.fixedTimeComparison(mac, authorization.mac)) {
+ return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials);
+ }
+
+ // Check payload hash
+
+ var hash = Crypto.calculatePayloadHash(message, credentials.algorithm);
+ if (!Cryptiles.fixedTimeComparison(hash, authorization.hash)) {
+ return callback(Boom.unauthorized('Bad message hash', 'Hawk'), credentials);
+ }
+
+ // Check nonce
+
+ options.nonceFunc(authorization.nonce, authorization.ts, function (err) {
+
+ if (err) {
+ return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials);
+ }
+
+ // Check timestamp staleness
+
+ if (Math.abs((authorization.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
+ return callback(Boom.unauthorized('Stale timestamp'), credentials);
+ }
+
+ // Successful authentication
+
+ return callback(null, credentials);
+ });
+ });
+};
--- /dev/null
+// Load modules
+
+var Sntp = require('sntp');
+var Boom = require('boom');
+
+
+// Declare internals
+
+var internals = {};
+
+
+exports.version = function () {
+
+ return require('../package.json').version;
+};
+
+
+// Extract host and port from request
+
+// $1 $2
+internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6)
+
+
+exports.parseHost = function (req, hostHeaderName) {
+
+ hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
+ var hostHeader = req.headers[hostHeaderName];
+ if (!hostHeader) {
+ return null;
+ }
+
+ var hostParts = hostHeader.match(internals.hostHeaderRegex);
+ if (!hostParts) {
+ return null;
+ }
+
+ return {
+ name: hostParts[1],
+ port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))
+ };
+};
+
+
+// Parse Content-Type header content
+
+exports.parseContentType = function (header) {
+
+ if (!header) {
+ return '';
+ }
+
+ return header.split(';')[0].trim().toLowerCase();
+};
+
+
+// Convert node's to request configuration object
+
+exports.parseRequest = function (req, options) {
+
+ if (!req.headers) {
+ return req;
+ }
+
+ // Obtain host and port information
+
+ if (!options.host || !options.port) {
+ var host = exports.parseHost(req, options.hostHeaderName);
+ if (!host) {
+ return new Error('Invalid Host header');
+ }
+ }
+
+ var request = {
+ method: req.method,
+ url: req.url,
+ host: options.host || host.name,
+ port: options.port || host.port,
+ authorization: req.headers.authorization,
+ contentType: req.headers['content-type'] || ''
+ };
+
+ return request;
+};
+
+
+exports.now = function (localtimeOffsetMsec) {
+
+ return Sntp.now() + (localtimeOffsetMsec || 0);
+};
+
+
+exports.nowSecs = function (localtimeOffsetMsec) {
+
+ return Math.floor(exports.now(localtimeOffsetMsec) / 1000);
+};
+
+
+// Parse Hawk HTTP Authorization header
+
+exports.parseAuthorizationHeader = function (header, keys) {
+
+ keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];
+
+ if (!header) {
+ return Boom.unauthorized(null, 'Hawk');
+ }
+
+ var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
+ if (!headerParts) {
+ return Boom.badRequest('Invalid header syntax');
+ }
+
+ var scheme = headerParts[1];
+ if (scheme.toLowerCase() !== 'hawk') {
+ return Boom.unauthorized(null, 'Hawk');
+ }
+
+ var attributesString = headerParts[2];
+ if (!attributesString) {
+ return Boom.badRequest('Invalid header syntax');
+ }
+
+ var attributes = {};
+ var errorMessage = '';
+ var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
+
+ // Check valid attribute names
+
+ if (keys.indexOf($1) === -1) {
+ errorMessage = 'Unknown attribute: ' + $1;
+ return;
+ }
+
+ // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
+
+ if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
+ errorMessage = 'Bad attribute value: ' + $1;
+ return;
+ }
+
+ // Check for duplicates
+
+ if (attributes.hasOwnProperty($1)) {
+ errorMessage = 'Duplicate attribute: ' + $1;
+ return;
+ }
+
+ attributes[$1] = $2;
+ return '';
+ });
+
+ if (verify !== '') {
+ return Boom.badRequest(errorMessage || 'Bad header format');
+ }
+
+ return attributes;
+};
+
+
+exports.unauthorized = function (message) {
+
+ return Boom.unauthorized(message, 'Hawk');
+};
+
--- /dev/null
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+
--- /dev/null
+language: node_js
+
+node_js:
+ - 0.10
+ - 0.12
+ - iojs
+
--- /dev/null
+Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md).
--- /dev/null
+Copyright (c) 2012-2014, Walmart and other contributors.
+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.
+ * The names of any contributors may not 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS 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.
+
+ * * *
+
+The complete list of contributors can be found at: https://github.com/hapijs/boom/graphs/contributors
\ No newline at end of file
--- /dev/null
+
+
+HTTP-friendly error objects
+
+[](http://travis-ci.org/hapijs/boom)
+[](https://www.npmjs.com/package/boom)
+
+Lead Maintainer: [Adam Bretz](https://github.com/arb)
+
+**boom** provides a set of utilities for returning HTTP errors. Each utility returns a `Boom` error response
+object (instance of `Error`) which includes the following properties:
+- `isBoom` - if `true`, indicates this is a `Boom` object instance.
+- `isServer` - convenience bool indicating status code >= 500.
+- `message` - the error message.
+- `output` - the formatted response. Can be directly manipulated after object construction to return a custom
+ error response. Allowed root keys:
+ - `statusCode` - the HTTP status code (typically 4xx or 5xx).
+ - `headers` - an object containing any HTTP headers where each key is a header name and value is the header content.
+ - `payload` - the formatted object used as the response payload (stringified). Can be directly manipulated but any
+ changes will be lost
+ if `reformat()` is called. Any content allowed and by default includes the following content:
+ - `statusCode` - the HTTP status code, derived from `error.output.statusCode`.
+ - `error` - the HTTP status message (e.g. 'Bad Request', 'Internal Server Error') derived from `statusCode`.
+ - `message` - the error message derived from `error.message`.
+- inherited `Error` properties.
+
+The `Boom` object also supports the following method:
+- `reformat()` - rebuilds `error.output` using the other object properties.
+
+## Helper Methods
+
+### `wrap(error, [statusCode], [message])`
+
+Decorates an error with the **boom** properties where:
+- `error` - the error object to wrap. If `error` is already a **boom** object, returns back the same object.
+- `statusCode` - optional HTTP status code. Defaults to `500`.
+- `message` - optional message string. If the error already has a message, it adds the message as a prefix.
+ Defaults to no message.
+
+```js
+var error = new Error('Unexpected input');
+Boom.wrap(error, 400);
+```
+
+### `create(statusCode, [message], [data])`
+
+Generates an `Error` object with the **boom** decorations where:
+- `statusCode` - an HTTP error code number. Must be greater or equal 400.
+- `message` - optional message string.
+- `data` - additional error data set to `error.data` property.
+
+```js
+var error = Boom.create(400, 'Bad request', { timestamp: Date.now() });
+```
+
+## HTTP 4xx Errors
+
+### `Boom.badRequest([message], [data])`
+
+Returns a 400 Bad Request error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.badRequest('invalid query');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 400,
+ "error": "Bad Request",
+ "message": "invalid query"
+}
+```
+
+### `Boom.unauthorized([message], [scheme], [attributes])`
+
+Returns a 401 Unauthorized error where:
+- `message` - optional message.
+- `scheme` can be one of the following:
+ - an authentication scheme name
+ - an array of string values. These values will be separated by ', ' and set to the 'WWW-Authenticate' header.
+- `attributes` - an object of values to use while setting the 'WWW-Authenticate' header. This value is only used when `schema` is a string, otherwise it is ignored. Every key/value pair will be included in the 'WWW-Authenticate' in the format of 'key="value"'. `null` and `undefined` will be replaced with an empty string. If `attributes` is set, `message` will be used as the 'error' segment of the 'WWW-Authenticate' header. If `message` is unset, the 'error' segment of the header will not be present and `isMissing` will be true on the error object.
+
+If either `scheme` or `attributes` are set, the resultant `Boom` object will have the 'WWW-Authenticate' header set for the response.
+
+```js
+Boom.unauthorized('invalid password');
+```
+
+Generates the following response:
+
+```json
+"payload": {
+ "statusCode": 401,
+ "error": "Unauthorized",
+ "message": "invalid password"
+},
+"headers" {}
+```
+
+```js
+Boom.unauthorized('invalid password', 'sample');
+```
+
+Generates the following response:
+
+```json
+"payload": {
+ "statusCode": 401,
+ "error": "Unauthorized",
+ "message": "invalid password"
+},
+"headers" {
+ "WWW-Authenticate": "sample error=\"invalid password\""
+}
+```
+
+```js
+Boom.unauthorized('invalid password', 'sample', { ttl: 0, cache: null, foo: 'bar' });
+```
+
+Generates the following response:
+
+```json
+"payload": {
+ "statusCode": 401,
+ "error": "Unauthorized",
+ "message": "invalid password"
+},
+"headers" {
+ "WWW-Authenticate": "sample ttl=\"0\", cache=\"\", foo=\"bar\", error=\"invalid password\""
+}
+```
+
+### `Boom.forbidden([message], [data])`
+
+Returns a 403 Forbidden error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.forbidden('try again some time');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 403,
+ "error": "Forbidden",
+ "message": "try again some time"
+}
+```
+
+### `Boom.notFound([message], [data])`
+
+Returns a 404 Not Found error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.notFound('missing');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 404,
+ "error": "Not Found",
+ "message": "missing"
+}
+```
+
+### `Boom.methodNotAllowed([message], [data])`
+
+Returns a 405 Method Not Allowed error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.methodNotAllowed('that method is not allowed');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 405,
+ "error": "Method Not Allowed",
+ "message": "that method is not allowed"
+}
+```
+
+### `Boom.notAcceptable([message], [data])`
+
+Returns a 406 Not Acceptable error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.notAcceptable('unacceptable');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 406,
+ "error": "Not Acceptable",
+ "message": "unacceptable"
+}
+```
+
+### `Boom.proxyAuthRequired([message], [data])`
+
+Returns a 407 Proxy Authentication Required error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.proxyAuthRequired('auth missing');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 407,
+ "error": "Proxy Authentication Required",
+ "message": "auth missing"
+}
+```
+
+### `Boom.clientTimeout([message], [data])`
+
+Returns a 408 Request Time-out error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.clientTimeout('timed out');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 408,
+ "error": "Request Time-out",
+ "message": "timed out"
+}
+```
+
+### `Boom.conflict([message], [data])`
+
+Returns a 409 Conflict error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.conflict('there was a conflict');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 409,
+ "error": "Conflict",
+ "message": "there was a conflict"
+}
+```
+
+### `Boom.resourceGone([message], [data])`
+
+Returns a 410 Gone error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.resourceGone('it is gone');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 410,
+ "error": "Gone",
+ "message": "it is gone"
+}
+```
+
+### `Boom.lengthRequired([message], [data])`
+
+Returns a 411 Length Required error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.lengthRequired('length needed');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 411,
+ "error": "Length Required",
+ "message": "length needed"
+}
+```
+
+### `Boom.preconditionFailed([message], [data])`
+
+Returns a 412 Precondition Failed error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.preconditionFailed();
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 412,
+ "error": "Precondition Failed"
+}
+```
+
+### `Boom.entityTooLarge([message], [data])`
+
+Returns a 413 Request Entity Too Large error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.entityTooLarge('too big');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 413,
+ "error": "Request Entity Too Large",
+ "message": "too big"
+}
+```
+
+### `Boom.uriTooLong([message], [data])`
+
+Returns a 414 Request-URI Too Large error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.uriTooLong('uri is too long');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 414,
+ "error": "Request-URI Too Large",
+ "message": "uri is too long"
+}
+```
+
+### `Boom.unsupportedMediaType([message], [data])`
+
+Returns a 415 Unsupported Media Type error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.unsupportedMediaType('that media is not supported');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 415,
+ "error": "Unsupported Media Type",
+ "message": "that media is not supported"
+}
+```
+
+### `Boom.rangeNotSatisfiable([message], [data])`
+
+Returns a 416 Requested Range Not Satisfiable error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.rangeNotSatisfiable();
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 416,
+ "error": "Requested Range Not Satisfiable"
+}
+```
+
+### `Boom.expectationFailed([message], [data])`
+
+Returns a 417 Expectation Failed error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.expectationFailed('expected this to work');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 417,
+ "error": "Expectation Failed",
+ "message": "expected this to work"
+}
+```
+
+### `Boom.badData([message], [data])`
+
+Returns a 422 Unprocessable Entity error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.badData('your data is bad and you should feel bad');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 422,
+ "error": "Unprocessable Entity",
+ "message": "your data is bad and you should feel bad"
+}
+```
+
+### `Boom.tooManyRequests([message], [data])`
+
+Returns a 429 Too Many Requests error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.tooManyRequests('you have exceeded your request limit');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 429,
+ "error": "Too Many Requests",
+ "message": "you have exceeded your request limit"
+}
+```
+
+## HTTP 5xx Errors
+
+All 500 errors hide your message from the end user. Your message is recorded in the server log.
+
+### `Boom.notImplemented([message], [data])`
+
+Returns a 501 Not Implemented error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.notImplemented('method not implemented');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 501,
+ "error": "Not Implemented",
+ "message": "method not implemented"
+}
+```
+
+### `Boom.badGateway([message], [data])`
+
+Returns a 502 Bad Gateway error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.badGateway('that is a bad gateway');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 502,
+ "error": "Bad Gateway",
+ "message": "that is a bad gateway"
+}
+```
+
+### `Boom.serverTimeout([message], [data])`
+
+Returns a 503 Service Unavailable error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.serverTimeout('unavailable');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 503,
+ "error": "Service Unavailable",
+ "message": "unavailable"
+}
+```
+
+### `Boom.gatewayTimeout([message], [data])`
+
+Returns a 504 Gateway Time-out error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.gatewayTimeout();
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 504,
+ "error": "Gateway Time-out"
+}
+```
+
+### `Boom.badImplementation([message], [data])`
+
+Returns a 500 Internal Server Error error where:
+- `message` - optional message.
+- `data` - optional additional error data.
+
+```js
+Boom.badImplementation('terrible implementation');
+```
+
+Generates the following response payload:
+
+```json
+{
+ "statusCode": 500,
+ "error": "Internal Server Error",
+ "message": "An internal server error occurred"
+}
+```
+
+## F.A.Q.
+
+###### How do I include extra information in my responses? `output.payload` is missing `data`, what gives?
+
+There is a reason the values passed back in the response payloads are pretty locked down. It's mostly for security and to not leak any important information back to the client. This means you will need to put in a little more effort to include extra information about your custom error. Check out the ["Error transformation"](https://github.com/hapijs/hapi/blob/master/API.md#error-transformation) section in the hapi documentation.
--- /dev/null
+module.exports = require('./lib');
\ No newline at end of file
--- /dev/null
+// Load modules
+
+var Http = require('http');
+var Hoek = require('hoek');
+
+
+// Declare internals
+
+var internals = {};
+
+
+exports.wrap = function (error, statusCode, message) {
+
+ Hoek.assert(error instanceof Error, 'Cannot wrap non-Error object');
+ return (error.isBoom ? error : internals.initialize(error, statusCode || 500, message));
+};
+
+
+exports.create = function (statusCode, message, data) {
+
+ var error = new Error(message ? message : undefined); // Avoids settings null message
+ error.data = data || null;
+ internals.initialize(error, statusCode);
+ return error;
+};
+
+
+internals.initialize = function (error, statusCode, message) {
+
+ var numberCode = parseInt(statusCode, 10);
+ Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode);
+
+ error.isBoom = true;
+ error.isServer = numberCode >= 500;
+
+ if (!error.hasOwnProperty('data')) {
+ error.data = null;
+ }
+
+ error.output = {
+ statusCode: numberCode,
+ payload: {},
+ headers: {}
+ };
+
+ error.reformat = internals.reformat;
+ error.reformat();
+
+ if (!message &&
+ !error.message) {
+
+ message = error.output.payload.error;
+ }
+
+ if (message) {
+ error.message = (message + (error.message ? ': ' + error.message : ''));
+ }
+
+ return error;
+};
+
+
+internals.reformat = function () {
+
+ this.output.payload.statusCode = this.output.statusCode;
+ this.output.payload.error = Http.STATUS_CODES[this.output.statusCode] || 'Unknown';
+
+ if (this.output.statusCode === 500) {
+ this.output.payload.message = 'An internal server error occurred'; // Hide actual error from user
+ }
+ else if (this.message) {
+ this.output.payload.message = this.message;
+ }
+};
+
+
+// 4xx Client Errors
+
+exports.badRequest = function (message, data) {
+
+ return exports.create(400, message, data);
+};
+
+
+exports.unauthorized = function (message, scheme, attributes) { // Or function (message, wwwAuthenticate[])
+
+ var err = exports.create(401, message);
+
+ if (!scheme) {
+ return err;
+ }
+
+ var wwwAuthenticate = '';
+ var i = 0;
+ var il = 0;
+
+ if (typeof scheme === 'string') {
+
+ // function (message, scheme, attributes)
+
+ wwwAuthenticate = scheme;
+ if (attributes) {
+ var names = Object.keys(attributes);
+ for (i = 0, il = names.length; i < il; ++i) {
+ if (i) {
+ wwwAuthenticate += ',';
+ }
+
+ var value = attributes[names[i]];
+ if (value === null ||
+ value === undefined) { // Value can be zero
+
+ value = '';
+ }
+ wwwAuthenticate += ' ' + names[i] + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"';
+ }
+ }
+
+ if (message) {
+ if (attributes) {
+ wwwAuthenticate += ',';
+ }
+ wwwAuthenticate += ' error="' + Hoek.escapeHeaderAttribute(message) + '"';
+ }
+ else {
+ err.isMissing = true;
+ }
+ }
+ else {
+
+ // function (message, wwwAuthenticate[])
+
+ var wwwArray = scheme;
+ for (i = 0, il = wwwArray.length; i < il; ++i) {
+ if (i) {
+ wwwAuthenticate += ', ';
+ }
+
+ wwwAuthenticate += wwwArray[i];
+ }
+ }
+
+ err.output.headers['WWW-Authenticate'] = wwwAuthenticate;
+
+ return err;
+};
+
+
+exports.forbidden = function (message, data) {
+
+ return exports.create(403, message, data);
+};
+
+
+exports.notFound = function (message, data) {
+
+ return exports.create(404, message, data);
+};
+
+
+exports.methodNotAllowed = function (message, data) {
+
+ return exports.create(405, message, data);
+};
+
+
+exports.notAcceptable = function (message, data) {
+
+ return exports.create(406, message, data);
+};
+
+
+exports.proxyAuthRequired = function (message, data) {
+
+ return exports.create(407, message, data);
+};
+
+
+exports.clientTimeout = function (message, data) {
+
+ return exports.create(408, message, data);
+};
+
+
+exports.conflict = function (message, data) {
+
+ return exports.create(409, message, data);
+};
+
+
+exports.resourceGone = function (message, data) {
+
+ return exports.create(410, message, data);
+};
+
+
+exports.lengthRequired = function (message, data) {
+
+ return exports.create(411, message, data);
+};
+
+
+exports.preconditionFailed = function (message, data) {
+
+ return exports.create(412, message, data);
+};
+
+
+exports.entityTooLarge = function (message, data) {
+
+ return exports.create(413, message, data);
+};
+
+
+exports.uriTooLong = function (message, data) {
+
+ return exports.create(414, message, data);
+};
+
+
+exports.unsupportedMediaType = function (message, data) {
+
+ return exports.create(415, message, data);
+};
+
+
+exports.rangeNotSatisfiable = function (message, data) {
+
+ return exports.create(416, message, data);
+};
+
+
+exports.expectationFailed = function (message, data) {
+
+ return exports.create(417, message, data);
+};
+
+exports.badData = function (message, data) {
+
+ return exports.create(422, message, data);
+};
+
+
+exports.tooManyRequests = function (message, data) {
+
+ return exports.create(429, message, data);
+};
+
+
+// 5xx Server Errors
+
+exports.internal = function (message, data, statusCode) {
+
+ var error = (data instanceof Error ? exports.wrap(data, statusCode, message) : exports.create(statusCode || 500, message));
+
+ if (data instanceof Error === false) {
+ error.data = data;
+ }
+
+ return error;
+};
+
+
+exports.notImplemented = function (message, data) {
+
+ return exports.internal(message, data, 501);
+};
+
+
+exports.badGateway = function (message, data) {
+
+ return exports.internal(message, data, 502);
+};
+
+
+exports.serverTimeout = function (message, data) {
+
+ return exports.internal(message, data, 503);
+};
+
+
+exports.gatewayTimeout = function (message, data) {
+
+ return exports.internal(message, data, 504);
+};
+
+
+exports.badImplementation = function (message, data) {
+
+ var err = exports.internal(message, data, 500);
+ err.isDeveloperError = true;
+ return err;
+};
--- /dev/null
+{
+ "name": "boom",
+ "description": "HTTP-friendly error objects",
+ "version": "2.7.2",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/hapijs/boom"
+ },
+ "main": "index",
+ "keywords": [
+ "error",
+ "http"
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "dependencies": {
+ "hoek": "2.x.x"
+ },
+ "devDependencies": {
+ "code": "1.x.x",
+ "lab": "5.x.x"
+ },
+ "scripts": {
+ "test": "lab -a code -t 100",
+ "test-cov-html": "lab -a code -r html -o coverage.html"
+ },
+ "license": "BSD-3-Clause",
+ "gitHead": "29be188e9ecfce3a2e090b8714dec2b6e0791724",
+ "bugs": {
+ "url": "https://github.com/hapijs/boom/issues"
+ },
+ "homepage": "https://github.com/hapijs/boom",
+ "_id": "boom@2.7.2",
+ "_shasum": "dad628d897f7fd2e32cc82197f13307971cf8354",
+ "_from": "boom@>=2.0.0 <3.0.0",
+ "_npmVersion": "2.7.0",
+ "_nodeVersion": "0.10.38",
+ "_npmUser": {
+ "name": "arb",
+ "email": "arbretz@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ },
+ {
+ "name": "wyatt",
+ "email": "wpreul@gmail.com"
+ },
+ {
+ "name": "arb",
+ "email": "arbretz@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "dad628d897f7fd2e32cc82197f13307971cf8354",
+ "tarball": "http://registry.npmjs.org/boom/-/boom-2.7.2.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/boom/-/boom-2.7.2.tgz"
+}
--- /dev/null
+// Load modules
+
+var Util = require('util');
+var Code = require('code');
+var Boom = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.describe;
+var it = lab.it;
+var expect = Code.expect;
+
+
+it('returns the same object when already boom', function (done) {
+
+ var error = Boom.badRequest();
+ var wrapped = Boom.wrap(error);
+ expect(error).to.equal(wrapped);
+ done();
+});
+
+it('returns an error with info when constructed using another error', function (done) {
+
+ var error = new Error('ka-boom');
+ error.xyz = 123;
+ var err = Boom.wrap(error);
+ expect(err.xyz).to.equal(123);
+ expect(err.message).to.equal('ka-boom');
+ expect(err.output).to.deep.equal({
+ statusCode: 500,
+ payload: {
+ statusCode: 500,
+ error: 'Internal Server Error',
+ message: 'An internal server error occurred'
+ },
+ headers: {}
+ });
+ expect(err.data).to.equal(null);
+ done();
+});
+
+it('does not override data when constructed using another error', function (done) {
+
+ var error = new Error('ka-boom');
+ error.data = { useful: 'data' };
+ var err = Boom.wrap(error);
+ expect(err.data).to.equal(error.data);
+ done();
+});
+
+it('sets new message when none exists', function (done) {
+
+ var error = new Error();
+ var wrapped = Boom.wrap(error, 400, 'something bad');
+ expect(wrapped.message).to.equal('something bad');
+ done();
+});
+
+it('throws when statusCode is not a number', function (done) {
+
+ expect(function () {
+
+ Boom.create('x');
+ }).to.throw('First argument must be a number (400+): x');
+ done();
+});
+
+it('will cast a number-string to an integer', function (done) {
+
+ var codes = [
+ { input: '404', result: 404 },
+ { input: '404.1', result: 404 },
+ { input: 400, result: 400 },
+ { input: 400.123, result: 400 }]
+ for (var i = 0, il = codes.length; i < il; ++i) {
+ var code = codes[i];
+ var err = Boom.create(code.input);
+ expect(err.output.statusCode).to.equal(code.result);
+ }
+
+ done();
+});
+
+it('throws when statusCode is not finite', function (done) {
+
+ expect(function () {
+
+ Boom.create(1 / 0);
+ }).to.throw('First argument must be a number (400+): null');
+ done();
+});
+
+it('sets error code to unknown', function (done) {
+
+ var err = Boom.create(999);
+ expect(err.output.payload.error).to.equal('Unknown');
+ done();
+});
+
+describe('create()', function () {
+
+ it('does not sets null message', function (done) {
+
+ var error = Boom.unauthorized(null);
+ expect(error.output.payload.message).to.not.exist();
+ expect(error.isServer).to.be.false();
+ done();
+ });
+
+ it('sets message and data', function (done) {
+
+ var error = Boom.badRequest('Missing data', { type: 'user' });
+ expect(error.data.type).to.equal('user');
+ expect(error.output.payload.message).to.equal('Missing data');
+ done();
+ });
+});
+
+describe('isBoom()', function () {
+
+ it('returns true for Boom object', function (done) {
+
+ expect(Boom.badRequest().isBoom).to.equal(true);
+ done();
+ });
+
+ it('returns false for Error object', function (done) {
+
+ expect((new Error()).isBoom).to.not.exist();
+ done();
+ });
+});
+
+describe('badRequest()', function () {
+
+ it('returns a 400 error statusCode', function (done) {
+
+ var error = Boom.badRequest();
+
+ expect(error.output.statusCode).to.equal(400);
+ expect(error.isServer).to.be.false();
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.badRequest('my message').message).to.equal('my message');
+ done();
+ });
+
+ it('sets the message to HTTP status if none provided', function (done) {
+
+ expect(Boom.badRequest().message).to.equal('Bad Request');
+ done();
+ });
+});
+
+describe('unauthorized()', function () {
+
+ it('returns a 401 error statusCode', function (done) {
+
+ var err = Boom.unauthorized();
+ expect(err.output.statusCode).to.equal(401);
+ expect(err.output.headers).to.deep.equal({});
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.unauthorized('my message').message).to.equal('my message');
+ done();
+ });
+
+ it('returns a WWW-Authenticate header when passed a scheme', function (done) {
+
+ var err = Boom.unauthorized('boom', 'Test');
+ expect(err.output.statusCode).to.equal(401);
+ expect(err.output.headers['WWW-Authenticate']).to.equal('Test error="boom"');
+ done();
+ });
+
+ it('returns a WWW-Authenticate header set to the schema array value', function (done) {
+
+ var err = Boom.unauthorized(null, ['Test','one','two']);
+ expect(err.output.statusCode).to.equal(401);
+ expect(err.output.headers['WWW-Authenticate']).to.equal('Test, one, two');
+ done();
+ });
+
+ it('returns a WWW-Authenticate header when passed a scheme and attributes', function (done) {
+
+ var err = Boom.unauthorized('boom', 'Test', { a: 1, b: 'something', c: null, d: 0 });
+ expect(err.output.statusCode).to.equal(401);
+ expect(err.output.headers['WWW-Authenticate']).to.equal('Test a="1", b="something", c="", d="0", error="boom"');
+ done();
+ });
+
+ it('returns a WWW-Authenticate header when passed attributes, missing error', function (done) {
+
+ var err = Boom.unauthorized(null, 'Test', { a: 1, b: 'something', c: null, d: 0 });
+ expect(err.output.statusCode).to.equal(401);
+ expect(err.output.headers['WWW-Authenticate']).to.equal('Test a="1", b="something", c="", d="0"');
+ expect(err.isMissing).to.equal(true);
+ done();
+ });
+
+ it('sets the isMissing flag when error message is empty', function (done) {
+
+ var err = Boom.unauthorized('', 'Basic');
+ expect(err.isMissing).to.equal(true);
+ done();
+ });
+
+ it('does not set the isMissing flag when error message is not empty', function (done) {
+
+ var err = Boom.unauthorized('message', 'Basic');
+ expect(err.isMissing).to.equal(undefined);
+ done();
+ });
+
+ it('sets a WWW-Authenticate when passed as an array', function (done) {
+
+ var err = Boom.unauthorized('message', ['Basic', 'Example e="1"', 'Another x="3", y="4"']);
+ expect(err.output.headers['WWW-Authenticate']).to.equal('Basic, Example e="1", Another x="3", y="4"');
+ done();
+ });
+});
+
+
+describe('methodNotAllowed()', function () {
+
+ it('returns a 405 error statusCode', function (done) {
+
+ expect(Boom.methodNotAllowed().output.statusCode).to.equal(405);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.methodNotAllowed('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('notAcceptable()', function () {
+
+ it('returns a 406 error statusCode', function (done) {
+
+ expect(Boom.notAcceptable().output.statusCode).to.equal(406);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.notAcceptable('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('proxyAuthRequired()', function () {
+
+ it('returns a 407 error statusCode', function (done) {
+
+ expect(Boom.proxyAuthRequired().output.statusCode).to.equal(407);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.proxyAuthRequired('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('clientTimeout()', function () {
+
+ it('returns a 408 error statusCode', function (done) {
+
+ expect(Boom.clientTimeout().output.statusCode).to.equal(408);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.clientTimeout('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('conflict()', function () {
+
+ it('returns a 409 error statusCode', function (done) {
+
+ expect(Boom.conflict().output.statusCode).to.equal(409);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.conflict('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('resourceGone()', function () {
+
+ it('returns a 410 error statusCode', function (done) {
+
+ expect(Boom.resourceGone().output.statusCode).to.equal(410);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.resourceGone('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('lengthRequired()', function () {
+
+ it('returns a 411 error statusCode', function (done) {
+
+ expect(Boom.lengthRequired().output.statusCode).to.equal(411);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.lengthRequired('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('preconditionFailed()', function () {
+
+ it('returns a 412 error statusCode', function (done) {
+
+ expect(Boom.preconditionFailed().output.statusCode).to.equal(412);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.preconditionFailed('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('entityTooLarge()', function () {
+
+ it('returns a 413 error statusCode', function (done) {
+
+ expect(Boom.entityTooLarge().output.statusCode).to.equal(413);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.entityTooLarge('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('uriTooLong()', function () {
+
+ it('returns a 414 error statusCode', function (done) {
+
+ expect(Boom.uriTooLong().output.statusCode).to.equal(414);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.uriTooLong('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('unsupportedMediaType()', function () {
+
+ it('returns a 415 error statusCode', function (done) {
+
+ expect(Boom.unsupportedMediaType().output.statusCode).to.equal(415);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.unsupportedMediaType('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('rangeNotSatisfiable()', function () {
+
+ it('returns a 416 error statusCode', function (done) {
+
+ expect(Boom.rangeNotSatisfiable().output.statusCode).to.equal(416);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.rangeNotSatisfiable('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('expectationFailed()', function () {
+
+ it('returns a 417 error statusCode', function (done) {
+
+ expect(Boom.expectationFailed().output.statusCode).to.equal(417);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.expectationFailed('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('badData()', function () {
+
+ it('returns a 422 error statusCode', function (done) {
+
+ expect(Boom.badData().output.statusCode).to.equal(422);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.badData('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('tooManyRequests()', function () {
+
+ it('returns a 429 error statusCode', function (done) {
+
+ expect(Boom.tooManyRequests().output.statusCode).to.equal(429);
+ done();
+ });
+
+ it('sets the message with the passed-in message', function (done) {
+
+ expect(Boom.tooManyRequests('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+describe('serverTimeout()', function () {
+
+ it('returns a 503 error statusCode', function (done) {
+
+ expect(Boom.serverTimeout().output.statusCode).to.equal(503);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.serverTimeout('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+describe('forbidden()', function () {
+
+ it('returns a 403 error statusCode', function (done) {
+
+ expect(Boom.forbidden().output.statusCode).to.equal(403);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.forbidden('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+describe('notFound()', function () {
+
+ it('returns a 404 error statusCode', function (done) {
+
+ expect(Boom.notFound().output.statusCode).to.equal(404);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.notFound('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+describe('internal()', function () {
+
+ it('returns a 500 error statusCode', function (done) {
+
+ expect(Boom.internal().output.statusCode).to.equal(500);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ var err = Boom.internal('my message');
+ expect(err.message).to.equal('my message');
+ expect(err.isServer).to.true();
+ expect(err.output.payload.message).to.equal('An internal server error occurred');
+ done();
+ });
+
+ it('passes data on the callback if its passed in', function (done) {
+
+ expect(Boom.internal('my message', { my: 'data' }).data.my).to.equal('data');
+ done();
+ });
+
+ it('returns an error with composite message', function (done) {
+
+ try {
+ JSON.parse('{');
+ }
+ catch (err) {
+ var boom = Boom.internal('Someting bad', err);
+ expect(boom.message).to.equal('Someting bad: Unexpected end of input');
+ expect(boom.isServer).to.be.true();
+ done();
+ }
+ });
+});
+
+describe('notImplemented()', function () {
+
+ it('returns a 501 error statusCode', function (done) {
+
+ expect(Boom.notImplemented().output.statusCode).to.equal(501);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.notImplemented('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+
+describe('badGateway()', function () {
+
+ it('returns a 502 error statusCode', function (done) {
+
+ expect(Boom.badGateway().output.statusCode).to.equal(502);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.badGateway('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+describe('gatewayTimeout()', function () {
+
+ it('returns a 504 error statusCode', function (done) {
+
+ expect(Boom.gatewayTimeout().output.statusCode).to.equal(504);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.gatewayTimeout('my message').message).to.equal('my message');
+ done();
+ });
+});
+
+describe('badImplementation()', function () {
+
+ it('returns a 500 error statusCode', function (done) {
+
+ var err = Boom.badImplementation();
+ expect(err.output.statusCode).to.equal(500);
+ expect(err.isDeveloperError).to.equal(true);
+ expect(err.isServer).to.be.true();
+ done();
+ });
+});
--- /dev/null
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+
--- /dev/null
+language: node_js
+
+node_js:
+ - 0.10
+
--- /dev/null
+Copyright (c) 2014, Eran Hammer and other contributors.
+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.
+ * The names of any contributors may not 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS 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.
+
+ * * *
+
+The complete list of contributors can be found at: https://github.com/hueniverse/cryptiles/graphs/contributors
--- /dev/null
+test:
+ node node_modules/lab/bin/lab
+test-cov:
+ node node_modules/lab/bin/lab -t 100
+test-cov-html:
+ node node_modules/lab/bin/lab -r html -o coverage.html
+
+.PHONY: test test-cov test-cov-html
+
--- /dev/null
+cryptiles
+=========
+
+General purpose crypto utilities
+
+[](http://travis-ci.org/hapijs/cryptiles)
+
+## Methods
+
+### `randomString(<Number> size)`
+Returns a cryptographically strong pseudo-random data string. Takes a size argument for the length of the string.
+
+### `fixedTimeComparison(<String> a, <String> b)`
+Compare two strings using fixed time algorithm (to prevent time-based analysis of MAC digest match). Returns `true` if the strings match, `false` if they differ.
--- /dev/null
+module.exports = require('./lib');
\ No newline at end of file
--- /dev/null
+// Load modules
+
+var Crypto = require('crypto');
+var Boom = require('boom');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Generate a cryptographically strong pseudo-random data
+
+exports.randomString = function (size) {
+
+ var buffer = exports.randomBits((size + 1) * 6);
+ if (buffer instanceof Error) {
+ return buffer;
+ }
+
+ var string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
+ return string.slice(0, size);
+};
+
+
+exports.randomBits = function (bits) {
+
+ if (!bits ||
+ bits < 0) {
+
+ return Boom.internal('Invalid random bits count');
+ }
+
+ var bytes = Math.ceil(bits / 8);
+ try {
+ return Crypto.randomBytes(bytes);
+ }
+ catch (err) {
+ return Boom.internal('Failed generating random bits: ' + err.message);
+ }
+};
+
+
+// Compare two strings using fixed time algorithm (to prevent time-based analysis of MAC digest match)
+
+exports.fixedTimeComparison = function (a, b) {
+
+ if (typeof a !== 'string' ||
+ typeof b !== 'string') {
+
+ return false;
+ }
+
+ var mismatch = (a.length === b.length ? 0 : 1);
+ if (mismatch) {
+ b = a;
+ }
+
+ for (var i = 0, il = a.length; i < il; ++i) {
+ var ac = a.charCodeAt(i);
+ var bc = b.charCodeAt(i);
+ mismatch |= (ac ^ bc);
+ }
+
+ return (mismatch === 0);
+};
+
+
--- /dev/null
+{
+ "name": "cryptiles",
+ "description": "General purpose crypto utilities",
+ "version": "2.0.4",
+ "author": {
+ "name": "Eran Hammer",
+ "email": "eran@hammer.io",
+ "url": "http://hueniverse.com"
+ },
+ "contributors": [],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/hapijs/cryptiles.git"
+ },
+ "main": "index",
+ "keywords": [
+ "cryptography",
+ "security",
+ "utilites"
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "dependencies": {
+ "boom": "2.x.x"
+ },
+ "devDependencies": {
+ "lab": "4.x.x"
+ },
+ "scripts": {
+ "test": "make test-cov"
+ },
+ "licenses": [
+ {
+ "type": "BSD",
+ "url": "http://github.com/hapijs/cryptiles/raw/master/LICENSE"
+ }
+ ],
+ "gitHead": "944263dbb628b9b3da542d35600d587f861eeaf0",
+ "bugs": {
+ "url": "https://github.com/hapijs/cryptiles/issues"
+ },
+ "homepage": "https://github.com/hapijs/cryptiles",
+ "_id": "cryptiles@2.0.4",
+ "_shasum": "09ea1775b9e1c7de7e60a99d42ab6f08ce1a1285",
+ "_from": "cryptiles@>=2.0.0 <3.0.0",
+ "_npmVersion": "1.4.23",
+ "_npmUser": {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ },
+ "maintainers": [
+ {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ }
+ ],
+ "dist": {
+ "shasum": "09ea1775b9e1c7de7e60a99d42ab6f08ce1a1285",
+ "tarball": "http://registry.npmjs.org/cryptiles/-/cryptiles-2.0.4.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.4.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+// Load modules
+
+var Lab = require('lab');
+var Cryptiles = require('../lib');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var before = lab.before;
+var after = lab.after;
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Lab.expect;
+
+
+describe('Cryptiles', function () {
+
+ describe('#randomString', function () {
+
+ it('should generate the right length string', function (done) {
+
+ for (var i = 1; i <= 1000; ++i) {
+ expect(Cryptiles.randomString(i).length).to.equal(i);
+ }
+
+ done();
+ });
+
+ it('returns an error on invalid bits size', function (done) {
+
+ expect(Cryptiles.randomString(99999999999999999999).message).to.equal('Failed generating random bits: Argument #1 must be number > 0');
+ done();
+ });
+ });
+
+ describe('#randomBits', function () {
+
+ it('returns an error on invalid input', function (done) {
+
+ expect(Cryptiles.randomBits(0).message).to.equal('Invalid random bits count');
+ done();
+ });
+ });
+
+ describe('#fixedTimeComparison', function () {
+
+ var a = Cryptiles.randomString(50000);
+ var b = Cryptiles.randomString(150000);
+
+ it('should take the same amount of time comparing different string sizes', function (done) {
+
+ var now = Date.now();
+ Cryptiles.fixedTimeComparison(b, a);
+ var t1 = Date.now() - now;
+
+ now = Date.now();
+ Cryptiles.fixedTimeComparison(b, b);
+ var t2 = Date.now() - now;
+
+ expect(t2 - t1).to.be.within(-20, 20);
+ done();
+ });
+
+ it('should return true for equal strings', function (done) {
+
+ expect(Cryptiles.fixedTimeComparison(a, a)).to.equal(true);
+ done();
+ });
+
+ it('should return false for different strings (size, a < b)', function (done) {
+
+ expect(Cryptiles.fixedTimeComparison(a, a + 'x')).to.equal(false);
+ done();
+ });
+
+ it('should return false for different strings (size, a > b)', function (done) {
+
+ expect(Cryptiles.fixedTimeComparison(a + 'x', a)).to.equal(false);
+ done();
+ });
+
+ it('should return false for different strings (size, a = b)', function (done) {
+
+ expect(Cryptiles.fixedTimeComparison(a + 'x', a + 'y')).to.equal(false);
+ done();
+ });
+
+ it('should return false when not a string', function (done) {
+
+ expect(Cryptiles.fixedTimeComparison('x', null)).to.equal(false);
+ done();
+ });
+
+ it('should return false when not a string (left)', function (done) {
+
+ expect(Cryptiles.fixedTimeComparison(null, 'x')).to.equal(false);
+ done();
+ });
+ });
+});
+
+
--- /dev/null
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+complexity.md
--- /dev/null
+language: node_js
+
+node_js:
+ - 0.10
+ - 0.12
+ - iojs
+
--- /dev/null
+Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md).
--- /dev/null
+Copyright (c) 2011-2014, Walmart and other contributors.
+Copyright (c) 2011, 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.
+ * The names of any contributors may not 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS 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.
+
+ * * *
+
+The complete list of contributors can be found at: https://github.com/hapijs/hapi/graphs/contributors
+Portions of this project were initially based on the Yahoo! Inc. Postmile project,
+published at https://github.com/yahoo/postmile.
--- /dev/null
+test:
+ @node node_modules/lab/bin/lab -a code
+test-cov:
+ @node node_modules/lab/bin/lab -a code -t 100 -L
+test-cov-html:
+ @node node_modules/lab/bin/lab -a code -r html -o coverage.html
+
+.PHONY: test test-cov test-cov-html
--- /dev/null
+
+
+Utility methods for the hapi ecosystem. This module is not intended to solve every problem for everyone, but rather as a central place to store hapi-specific methods. If you're looking for a general purpose utility module, check out [lodash](https://github.com/lodash/lodash) or [underscore](https://github.com/jashkenas/underscore).
+
+[](http://travis-ci.org/hapijs/hoek)
+
+Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf)
+
+# Table of Contents
+
+* [Introduction](#introduction "Introduction")
+* [Object](#object "Object")
+ * [clone](#cloneobj "clone")
+ * [cloneWithShallow](#clonewithshallowobj-keys "cloneWithShallow")
+ * [merge](#mergetarget-source-isnulloverride-ismergearrays "merge")
+ * [applyToDefaults](#applytodefaultsdefaults-options-isnulloverride "applyToDefaults")
+ * [applyToDefaultsWithShallow](#applytodefaultswithshallowdefaults-options-keys "applyToDefaultsWithShallow")
+ * [deepEqual](#deepequala-b "deepEqual")
+ * [unique](#uniquearray-key "unique")
+ * [mapToObject](#maptoobjectarray-key "mapToObject")
+ * [intersect](#intersectarray1-array2 "intersect")
+ * [contain](#containref-values-options "contain")
+ * [flatten](#flattenarray-target "flatten")
+ * [reach](#reachobj-chain-options "reach")
+ * [reachTemplate](#reachobj-template-options "reachTemplate")
+ * [transform](#transformobj-transform-options "transform")
+ * [shallow](#shallowobj "shallow")
+ * [stringify](#stringifyobj "stringify")
+* [Timer](#timer "Timer")
+* [Bench](#bench "Bench")
+* [Binary Encoding/Decoding](#binary-encodingdecoding "Binary Encoding/Decoding")
+ * [base64urlEncode](#base64urlencodevalue "binary64urlEncode")
+ * [base64urlDecode](#base64urldecodevalue "binary64urlDecode")
+* [Escaping Characters](#escaping-characters "Escaping Characters")
+ * [escapeHtml](#escapehtmlstring "escapeHtml")
+ * [escapeHeaderAttribute](#escapeheaderattributeattribute "escapeHeaderAttribute")
+ * [escapeRegex](#escaperegexstring "escapeRegex")
+* [Errors](#errors "Errors")
+ * [assert](#assertcondition-message "assert")
+ * [abort](#abortmessage "abort")
+ * [displayStack](#displaystackslice "displayStack")
+ * [callStack](#callstackslice "callStack")
+* [Function](#function "Function")
+ * [nextTick](#nexttickfn "nextTick")
+ * [once](#oncefn "once")
+ * [ignore](#ignore "ignore")
+* [Miscellaneous](#miscellaneous "Miscellaneous")
+ * [uniqueFilename](#uniquefilename "uniqueFilename")
+ * [isInteger](#isInteger "isInteger")
+
+
+
+# Introduction
+
+The *Hoek* library contains some common functions used within the hapi ecosystem. It comes with useful methods for Arrays (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more.
+
+For example, to use Hoek to set configuration with default options:
+```javascript
+var Hoek = require('hoek');
+
+var default = {url : "www.github.com", port : "8000", debug : true};
+
+var config = Hoek.applyToDefaults(default, {port : "3000", admin : true});
+
+// In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true }
+```
+
+Under each of the sections (such as Array), there are subsections which correspond to Hoek methods. Each subsection will explain how to use the corresponding method. In each js excerpt below, the `var Hoek = require('hoek');` is omitted for brevity.
+
+## Object
+
+Hoek provides several helpful methods for objects and arrays.
+
+### clone(obj)
+
+This method is used to clone an object or an array. A *deep copy* is made (duplicates everything, including values that are objects, as well as non-enumerable properties).
+
+```javascript
+
+var nestedObj = {
+ w: /^something$/ig,
+ x: {
+ a: [1, 2, 3],
+ b: 123456,
+ c: new Date()
+ },
+ y: 'y',
+ z: new Date()
+ };
+
+var copy = Hoek.clone(nestedObj);
+
+copy.x.b = 100;
+
+console.log(copy.y); // results in 'y'
+console.log(nestedObj.x.b); // results in 123456
+console.log(copy.x.b); // results in 100
+```
+
+### cloneWithShallow(obj, keys)
+keys is an array of key names to shallow copy
+
+This method is also used to clone an object or array, however any keys listed in the `keys` array are shallow copied while those not listed are deep copied.
+
+```javascript
+
+var nestedObj = {
+ w: /^something$/ig,
+ x: {
+ a: [1, 2, 3],
+ b: 123456,
+ c: new Date()
+ },
+ y: 'y',
+ z: new Date()
+ };
+
+var copy = Hoek.cloneWithShallow(nestedObj, ['x']);
+
+copy.x.b = 100;
+
+console.log(copy.y); // results in 'y'
+console.log(nestedObj.x.b); // results in 100
+console.log(copy.x.b); // results in 100
+```
+
+### merge(target, source, isNullOverride, isMergeArrays)
+isNullOverride, isMergeArrays default to true
+
+Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied.
+Merge is destructive where the target is modified. For non destructive merge, use `applyToDefaults`.
+
+
+```javascript
+
+var target = {a: 1, b : 2};
+var source = {a: 0, c: 5};
+var source2 = {a: null, c: 5};
+
+Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5}
+Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5}
+Hoek.merge(target, source2, false); // results in {a: 1, b: 2, c: 5}
+
+var targetArray = [1, 2, 3];
+var sourceArray = [4, 5];
+
+Hoek.merge(targetArray, sourceArray); // results in [1, 2, 3, 4, 5]
+Hoek.merge(targetArray, sourceArray, true, false); // results in [4, 5]
+```
+
+### applyToDefaults(defaults, options, isNullOverride)
+isNullOverride defaults to false
+
+Apply options to a copy of the defaults
+
+```javascript
+
+var defaults = { host: "localhost", port: 8000 };
+var options = { port: 8080 };
+
+var config = Hoek.applyToDefaults(defaults, options); // results in { host: "localhost", port: 8080 }
+```
+
+Apply options with a null value to a copy of the defaults
+
+```javascript
+
+var defaults = { host: "localhost", port: 8000 };
+var options = { host: null, port: 8080 };
+
+var config = Hoek.applyToDefaults(defaults, options, true); // results in { host: null, port: 8080 }
+```
+
+### applyToDefaultsWithShallow(defaults, options, keys)
+keys is an array of key names to shallow copy
+
+Apply options to a copy of the defaults. Keys specified in the last parameter are shallow copied from options instead of merged.
+
+```javascript
+
+var defaults = {
+ server: {
+ host: "localhost",
+ port: 8000
+ },
+ name: 'example'
+ };
+
+var options = { server: { port: 8080 } };
+
+var config = Hoek.applyToDefaults(defaults, options); // results in { server: { port: 8080 }, name: 'example' }
+```
+
+### deepEqual(b, a, [options])
+
+Performs a deep comparison of the two values including support for circular dependencies, prototype, and properties. To skip prototype comparisons, use `options.prototype = false`
+
+```javascript
+Hoek.deepEqual({ a: [1, 2], b: 'string', c: { d: true } }, { a: [1, 2], b: 'string', c: { d: true } }); //results in true
+Hoek.deepEqual(Object.create(null), {}, { prototype: false }); //results in true
+Hoek.deepEqual(Object.create(null), {}); //results in false
+```
+
+### unique(array, key)
+
+Remove duplicate items from Array
+
+```javascript
+
+var array = [1, 2, 2, 3, 3, 4, 5, 6];
+
+var newArray = Hoek.unique(array); // results in [1,2,3,4,5,6]
+
+array = [{id: 1}, {id: 1}, {id: 2}];
+
+newArray = Hoek.unique(array, "id"); // results in [{id: 1}, {id: 2}]
+```
+
+### mapToObject(array, key)
+
+Convert an Array into an Object
+
+```javascript
+
+var array = [1,2,3];
+var newObject = Hoek.mapToObject(array); // results in [{"1": true}, {"2": true}, {"3": true}]
+
+array = [{id: 1}, {id: 2}];
+newObject = Hoek.mapToObject(array, "id"); // results in [{"id": 1}, {"id": 2}]
+```
+
+### intersect(array1, array2)
+
+Find the common unique items in two arrays
+
+```javascript
+
+var array1 = [1, 2, 3];
+var array2 = [1, 4, 5];
+
+var newArray = Hoek.intersect(array1, array2); // results in [1]
+```
+
+### contain(ref, values, [options])
+
+Tests if the reference value contains the provided values where:
+- `ref` - the reference string, array, or object.
+- `values` - a single or array of values to find within the `ref` value. If `ref` is an object, `values` can be a key name,
+ an array of key names, or an object with key-value pairs to compare.
+- `options` - an optional object with the following optional settings:
+ - `deep` - if `true`, performed a deep comparison of the values.
+ - `once` - if `true`, allows only one occurrence of each value.
+ - `only` - if `true`, does not allow values not explicitly listed.
+ - `part` - if `true`, allows partial match of the values (at least one must always match).
+
+Note: comparing a string to overlapping values will result in failed comparison (e.g. `contain('abc', ['ab', 'bc'])`).
+Also, if an object key's value does not match the provided value, `false` is returned even when `part` is specified.
+
+```javascript
+Hoek.contain('aaa', 'a', { only: true }); // true
+Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true }); // true
+Hoek.contain([1, 2, 2], [1, 2], { once: true }); // false
+Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true }); // true
+```
+
+### flatten(array, [target])
+
+Flatten an array
+
+```javascript
+
+var array = [1, [2, 3]];
+
+var flattenedArray = Hoek.flatten(array); // results in [1, 2, 3]
+
+array = [1, [2, 3]];
+target = [4, [5]];
+
+flattenedArray = Hoek.flatten(array, target); // results in [4, [5], 1, 2, 3]
+```
+
+### reach(obj, chain, [options])
+
+Converts an object key chain string to reference
+
+- `options` - optional settings
+ - `separator` - string to split chain path on, defaults to '.'
+ - `default` - value to return if the path or value is not present, default is `undefined`
+ - `strict` - if `true`, will throw an error on missing member, default is `false`
+ - `functions` - if `true` allow traversing functions for properties. `false` will throw an error if a function is part of the chain.
+
+A chain including negative numbers will work like negative indices on an
+array.
+
+```javascript
+
+var chain = 'a.b.c';
+var obj = {a : {b : { c : 1}}};
+
+Hoek.reach(obj, chain); // returns 1
+
+var chain = 'a.b.-1';
+var obj = {a : {b : [2,3,6]}};
+
+Hoek.reach(obj, chain); // returns 6
+```
+
+### reachTemplate(obj, template, [options])
+
+Replaces string parameters (`{name}`) with their corresponding object key values by applying the
+(`reach()`)[#reachobj-chain-options] method where:
+
+- `obj` - the context object used for key lookup.
+- `template` - a string containing `{}` parameters.
+- `options` - optional (`reach()`)[#reachobj-chain-options] options.
+
+```javascript
+
+var chain = 'a.b.c';
+var obj = {a : {b : { c : 1}}};
+
+Hoek.reachTemplate(obj, '1+{a.b.c}=2'); // returns '1+1=2'
+```
+
+### transform(obj, transform, [options])
+
+Transforms an existing object into a new one based on the supplied `obj` and `transform` map. `options` are the same as the `reach` options.
+
+```javascript
+var source = {
+ address: {
+ one: '123 main street',
+ two: 'PO Box 1234'
+ },
+ title: 'Warehouse',
+ state: 'CA'
+};
+
+var result = Hoek.transform(source, {
+ 'person.address.lineOne': 'address.one',
+ 'person.address.lineTwo': 'address.two',
+ 'title': 'title',
+ 'person.address.region': 'state'
+});
+// Results in
+// {
+// person: {
+// address: {
+// lineOne: '123 main street',
+// lineTwo: 'PO Box 1234',
+// region: 'CA'
+// }
+// },
+// title: 'Warehouse'
+// }
+```
+
+### shallow(obj)
+
+Performs a shallow copy by copying the references of all the top level children where:
+- `obj` - the object to be copied.
+
+```javascript
+var shallow = Hoek.shallow({ a: { b: 1 } });
+```
+
+### stringify(obj)
+
+Converts an object to string using the built-in `JSON.stringify()` method with the difference that any errors are caught
+and reported back in the form of the returned string. Used as a shortcut for displaying information to the console (e.g. in
+error message) without the need to worry about invalid conversion.
+
+```javascript
+var a = {};
+a.b = a;
+Hoek.stringify(a); // Returns '[Cannot display object: Converting circular structure to JSON]'
+```
+
+# Timer
+
+A Timer object. Initializing a new timer object sets the ts to the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.
+
+```javascript
+
+var timerObj = new Hoek.Timer();
+console.log("Time is now: " + timerObj.ts);
+console.log("Elapsed time from initialization: " + timerObj.elapsed() + 'milliseconds');
+```
+
+
+# Bench
+
+Same as Timer with the exception that `ts` stores the internal node clock which is not related to `Date.now()` and cannot be used to display
+human-readable timestamps. More accurate for benchmarking or internal timers.
+
+# Binary Encoding/Decoding
+
+### base64urlEncode(value)
+
+Encodes value in Base64 or URL encoding
+
+### base64urlDecode(value)
+
+Decodes data in Base64 or URL encoding.
+# Escaping Characters
+
+Hoek provides convenient methods for escaping html characters. The escaped characters are as followed:
+
+```javascript
+
+internals.htmlEscaped = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '`': '`'
+};
+```
+
+### escapeHtml(string)
+
+```javascript
+
+var string = '<html> hey </html>';
+var escapedString = Hoek.escapeHtml(string); // returns <html> hey </html>
+```
+
+### escapeHeaderAttribute(attribute)
+
+Escape attribute value for use in HTTP header
+
+```javascript
+
+var a = Hoek.escapeHeaderAttribute('I said "go w\\o me"'); //returns I said \"go w\\o me\"
+```
+
+
+### escapeRegex(string)
+
+Escape string for Regex construction
+
+```javascript
+
+var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); // returns 4\^f\$s\.4\*5\+\-_\?%\=#\!\:@\|~\\\/`"\(>\)\[<\]d\{\}s\,
+```
+
+# Errors
+
+### assert(condition, message)
+
+```javascript
+
+var a = 1, b = 2;
+
+Hoek.assert(a === b, 'a should equal b'); // Throws 'a should equal b'
+```
+
+Note that you may also pass an already created Error object as the second parameter, and `assert` will throw that object.
+
+```javascript
+
+var a = 1, b = 2;
+
+Hoek.assert(a === b, new Error('a should equal b')); // Throws the given error object
+```
+
+### abort(message)
+
+First checks if `process.env.NODE_ENV === 'test'`, and if so, throws error message. Otherwise,
+displays most recent stack and then exits process.
+
+
+
+### displayStack(slice)
+
+Displays the trace stack
+
+```javascript
+
+var stack = Hoek.displayStack();
+console.log(stack); // returns something like:
+
+[ 'null (/Users/user/Desktop/hoek/test.js:4:18)',
+ 'Module._compile (module.js:449:26)',
+ 'Module._extensions..js (module.js:467:10)',
+ 'Module.load (module.js:356:32)',
+ 'Module._load (module.js:312:12)',
+ 'Module.runMain (module.js:492:10)',
+ 'startup.processNextTick.process._tickCallback (node.js:244:9)' ]
+```
+
+### callStack(slice)
+
+Returns a trace stack array.
+
+```javascript
+
+var stack = Hoek.callStack();
+console.log(stack); // returns something like:
+
+[ [ '/Users/user/Desktop/hoek/test.js', 4, 18, null, false ],
+ [ 'module.js', 449, 26, 'Module._compile', false ],
+ [ 'module.js', 467, 10, 'Module._extensions..js', false ],
+ [ 'module.js', 356, 32, 'Module.load', false ],
+ [ 'module.js', 312, 12, 'Module._load', false ],
+ [ 'module.js', 492, 10, 'Module.runMain', false ],
+ [ 'node.js',
+ 244,
+ 9,
+ 'startup.processNextTick.process._tickCallback',
+ false ] ]
+```
+
+## Function
+
+### nextTick(fn)
+
+Returns a new function that wraps `fn` in `process.nextTick`.
+
+```javascript
+
+var myFn = function () {
+ console.log('Do this later');
+};
+
+var nextFn = Hoek.nextTick(myFn);
+
+nextFn();
+console.log('Do this first');
+
+// Results in:
+//
+// Do this first
+// Do this later
+```
+
+### once(fn)
+
+Returns a new function that can be run multiple times, but makes sure `fn` is only run once.
+
+```javascript
+
+var myFn = function () {
+ console.log('Ran myFn');
+};
+
+var onceFn = Hoek.once(myFn);
+onceFn(); // results in "Ran myFn"
+onceFn(); // results in undefined
+```
+
+### ignore
+
+A simple no-op function. It does nothing at all.
+
+## Miscellaneous
+
+### uniqueFilename(path, extension)
+`path` to prepend with the randomly generated file name. `extension` is the optional file extension, defaults to `''`.
+
+Returns a randomly generated file name at the specified `path`. The result is a fully resolved path to a file.
+
+```javascript
+var result = Hoek.uniqueFilename('./test/modules', 'txt'); // results in "full/path/test/modules/{random}.txt"
+```
+
+### isInteger(value)
+
+Check `value` to see if it is an integer. Returns true/false.
+
+```javascript
+var result = Hoek.isInteger('23')
+```
--- /dev/null
+module.exports = require('./lib');
--- /dev/null
+// Declare internals
+
+var internals = {};
+
+
+exports.escapeJavaScript = function (input) {
+
+ if (!input) {
+ return '';
+ }
+
+ var escaped = '';
+
+ for (var i = 0, il = input.length; i < il; ++i) {
+
+ var charCode = input.charCodeAt(i);
+
+ if (internals.isSafe(charCode)) {
+ escaped += input[i];
+ }
+ else {
+ escaped += internals.escapeJavaScriptChar(charCode);
+ }
+ }
+
+ return escaped;
+};
+
+
+exports.escapeHtml = function (input) {
+
+ if (!input) {
+ return '';
+ }
+
+ var escaped = '';
+
+ for (var i = 0, il = input.length; i < il; ++i) {
+
+ var charCode = input.charCodeAt(i);
+
+ if (internals.isSafe(charCode)) {
+ escaped += input[i];
+ }
+ else {
+ escaped += internals.escapeHtmlChar(charCode);
+ }
+ }
+
+ return escaped;
+};
+
+
+internals.escapeJavaScriptChar = function (charCode) {
+
+ if (charCode >= 256) {
+ return '\\u' + internals.padLeft('' + charCode, 4);
+ }
+
+ var hexValue = new Buffer(String.fromCharCode(charCode), 'ascii').toString('hex');
+ return '\\x' + internals.padLeft(hexValue, 2);
+};
+
+
+internals.escapeHtmlChar = function (charCode) {
+
+ var namedEscape = internals.namedHtml[charCode];
+ if (typeof namedEscape !== 'undefined') {
+ return namedEscape;
+ }
+
+ if (charCode >= 256) {
+ return '&#' + charCode + ';';
+ }
+
+ var hexValue = new Buffer(String.fromCharCode(charCode), 'ascii').toString('hex');
+ return '&#x' + internals.padLeft(hexValue, 2) + ';';
+};
+
+
+internals.padLeft = function (str, len) {
+
+ while (str.length < len) {
+ str = '0' + str;
+ }
+
+ return str;
+};
+
+
+internals.isSafe = function (charCode) {
+
+ return (typeof internals.safeCharCodes[charCode] !== 'undefined');
+};
+
+
+internals.namedHtml = {
+ '38': '&',
+ '60': '<',
+ '62': '>',
+ '34': '"',
+ '160': ' ',
+ '162': '¢',
+ '163': '£',
+ '164': '¤',
+ '169': '©',
+ '174': '®'
+};
+
+
+internals.safeCharCodes = (function () {
+
+ var safe = {};
+
+ for (var i = 32; i < 123; ++i) {
+
+ if ((i >= 97) || // a-z
+ (i >= 65 && i <= 90) || // A-Z
+ (i >= 48 && i <= 57) || // 0-9
+ i === 32 || // space
+ i === 46 || // .
+ i === 44 || // ,
+ i === 45 || // -
+ i === 58 || // :
+ i === 95) { // _
+
+ safe[i] = null;
+ }
+ }
+
+ return safe;
+}());
--- /dev/null
+// Load modules
+
+var Crypto = require('crypto');
+var Path = require('path');
+var Util = require('util');
+var Escape = require('./escape');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Clone object or array
+
+exports.clone = function (obj, seen) {
+
+ if (typeof obj !== 'object' ||
+ obj === null) {
+
+ return obj;
+ }
+
+ seen = seen || { orig: [], copy: [] };
+
+ var lookup = seen.orig.indexOf(obj);
+ if (lookup !== -1) {
+ return seen.copy[lookup];
+ }
+
+ var newObj;
+ var cloneDeep = false;
+
+ if (!Array.isArray(obj)) {
+ if (Buffer.isBuffer(obj)) {
+ newObj = new Buffer(obj);
+ }
+ else if (obj instanceof Date) {
+ newObj = new Date(obj.getTime());
+ }
+ else if (obj instanceof RegExp) {
+ newObj = new RegExp(obj);
+ }
+ else {
+ var proto = Object.getPrototypeOf(obj);
+ if (proto &&
+ proto.isImmutable) {
+
+ newObj = obj;
+ }
+ else {
+ newObj = Object.create(proto);
+ cloneDeep = true;
+ }
+ }
+ }
+ else {
+ newObj = [];
+ cloneDeep = true;
+ }
+
+ seen.orig.push(obj);
+ seen.copy.push(newObj);
+
+ if (cloneDeep) {
+ var keys = Object.getOwnPropertyNames(obj);
+ for (var i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ var descriptor = Object.getOwnPropertyDescriptor(obj, key);
+ if (descriptor.get ||
+ descriptor.set) {
+
+ Object.defineProperty(newObj, key, descriptor);
+ }
+ else {
+ newObj[key] = exports.clone(obj[key], seen);
+ }
+ }
+ }
+
+ return newObj;
+};
+
+
+// Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied
+/*eslint-disable */
+exports.merge = function (target, source, isNullOverride /* = true */, isMergeArrays /* = true */) {
+/*eslint-enable */
+ exports.assert(target && typeof target === 'object', 'Invalid target value: must be an object');
+ exports.assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
+
+ if (!source) {
+ return target;
+ }
+
+ if (Array.isArray(source)) {
+ exports.assert(Array.isArray(target), 'Cannot merge array onto an object');
+ if (isMergeArrays === false) { // isMergeArrays defaults to true
+ target.length = 0; // Must not change target assignment
+ }
+
+ for (var i = 0, il = source.length; i < il; ++i) {
+ target.push(exports.clone(source[i]));
+ }
+
+ return target;
+ }
+
+ var keys = Object.keys(source);
+ for (var k = 0, kl = keys.length; k < kl; ++k) {
+ var key = keys[k];
+ var value = source[key];
+ if (value &&
+ typeof value === 'object') {
+
+ if (!target[key] ||
+ typeof target[key] !== 'object' ||
+ (Array.isArray(target[key]) ^ Array.isArray(value)) ||
+ value instanceof Date ||
+ Buffer.isBuffer(value) ||
+ value instanceof RegExp) {
+
+ target[key] = exports.clone(value);
+ }
+ else {
+ exports.merge(target[key], value, isNullOverride, isMergeArrays);
+ }
+ }
+ else {
+ if (value !== null &&
+ value !== undefined) { // Explicit to preserve empty strings
+
+ target[key] = value;
+ }
+ else if (isNullOverride !== false) { // Defaults to true
+ target[key] = value;
+ }
+ }
+ }
+
+ return target;
+};
+
+
+// Apply options to a copy of the defaults
+
+exports.applyToDefaults = function (defaults, options, isNullOverride) {
+
+ exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
+ exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
+
+ if (!options) { // If no options, return null
+ return null;
+ }
+
+ var copy = exports.clone(defaults);
+
+ if (options === true) { // If options is set to true, use defaults
+ return copy;
+ }
+
+ return exports.merge(copy, options, isNullOverride === true, false);
+};
+
+
+// Clone an object except for the listed keys which are shallow copied
+
+exports.cloneWithShallow = function (source, keys) {
+
+ if (!source ||
+ typeof source !== 'object') {
+
+ return source;
+ }
+
+ var storage = internals.store(source, keys); // Move shallow copy items to storage
+ var copy = exports.clone(source); // Deep copy the rest
+ internals.restore(copy, source, storage); // Shallow copy the stored items and restore
+ return copy;
+};
+
+
+internals.store = function (source, keys) {
+
+ var storage = {};
+ for (var i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ var value = exports.reach(source, key);
+ if (value !== undefined) {
+ storage[key] = value;
+ internals.reachSet(source, key, undefined);
+ }
+ }
+
+ return storage;
+};
+
+
+internals.restore = function (copy, source, storage) {
+
+ var keys = Object.keys(storage);
+ for (var i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ internals.reachSet(copy, key, storage[key]);
+ internals.reachSet(source, key, storage[key]);
+ }
+};
+
+
+internals.reachSet = function (obj, key, value) {
+
+ var path = key.split('.');
+ var ref = obj;
+ for (var i = 0, il = path.length; i < il; ++i) {
+ var segment = path[i];
+ if (i + 1 === il) {
+ ref[segment] = value;
+ }
+
+ ref = ref[segment];
+ }
+};
+
+
+// Apply options to defaults except for the listed keys which are shallow copied from option without merging
+
+exports.applyToDefaultsWithShallow = function (defaults, options, keys) {
+
+ exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
+ exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object');
+ exports.assert(keys && Array.isArray(keys), 'Invalid keys');
+
+ if (!options) { // If no options, return null
+ return null;
+ }
+
+ var copy = exports.cloneWithShallow(defaults, keys);
+
+ if (options === true) { // If options is set to true, use defaults
+ return copy;
+ }
+
+ var storage = internals.store(options, keys); // Move shallow copy items to storage
+ exports.merge(copy, options, false, false); // Deep copy the rest
+ internals.restore(copy, options, storage); // Shallow copy the stored items and restore
+ return copy;
+};
+
+
+// Deep object or array comparison
+
+exports.deepEqual = function (obj, ref, options, seen) {
+
+ options = options || { prototype: true };
+
+ var type = typeof obj;
+
+ if (type !== typeof ref) {
+ return false;
+ }
+
+ if (type !== 'object' ||
+ obj === null ||
+ ref === null) {
+
+ if (obj === ref) { // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql
+ return obj !== 0 || 1 / obj === 1 / ref; // -0 / +0
+ }
+
+ return obj !== obj && ref !== ref; // NaN
+ }
+
+ seen = seen || [];
+ if (seen.indexOf(obj) !== -1) {
+ return true; // If previous comparison failed, it would have stopped execution
+ }
+
+ seen.push(obj);
+
+ if (Array.isArray(obj)) {
+ if (!Array.isArray(ref)) {
+ return false;
+ }
+
+ if (!options.part && obj.length !== ref.length) {
+ return false;
+ }
+
+ for (var i = 0, il = obj.length; i < il; ++i) {
+ if (options.part) {
+ var found = false;
+ for (var r = 0, rl = ref.length; r < rl; ++r) {
+ if (exports.deepEqual(obj[i], ref[r], options, seen)) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+ }
+
+ if (!exports.deepEqual(obj[i], ref[i], options, seen)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ if (Buffer.isBuffer(obj)) {
+ if (!Buffer.isBuffer(ref)) {
+ return false;
+ }
+
+ if (obj.length !== ref.length) {
+ return false;
+ }
+
+ for (var j = 0, jl = obj.length; j < jl; ++j) {
+ if (obj[j] !== ref[j]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ if (obj instanceof Date) {
+ return (ref instanceof Date && obj.getTime() === ref.getTime());
+ }
+
+ if (obj instanceof RegExp) {
+ return (ref instanceof RegExp && obj.toString() === ref.toString());
+ }
+
+ if (options.prototype) {
+ if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) {
+ return false;
+ }
+ }
+
+ var keys = Object.getOwnPropertyNames(obj);
+
+ if (!options.part && keys.length !== Object.getOwnPropertyNames(ref).length) {
+ return false;
+ }
+
+ for (var k = 0, kl = keys.length; k < kl; ++k) {
+ var key = keys[k];
+ var descriptor = Object.getOwnPropertyDescriptor(obj, key);
+ if (descriptor.get) {
+ if (!exports.deepEqual(descriptor, Object.getOwnPropertyDescriptor(ref, key), options, seen)) {
+ return false;
+ }
+ }
+ else if (!exports.deepEqual(obj[key], ref[key], options, seen)) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+
+// Remove duplicate items from array
+
+exports.unique = function (array, key) {
+
+ var index = {};
+ var result = [];
+
+ for (var i = 0, il = array.length; i < il; ++i) {
+ var id = (key ? array[i][key] : array[i]);
+ if (index[id] !== true) {
+
+ result.push(array[i]);
+ index[id] = true;
+ }
+ }
+
+ return result;
+};
+
+
+// Convert array into object
+
+exports.mapToObject = function (array, key) {
+
+ if (!array) {
+ return null;
+ }
+
+ var obj = {};
+ for (var i = 0, il = array.length; i < il; ++i) {
+ if (key) {
+ if (array[i][key]) {
+ obj[array[i][key]] = true;
+ }
+ }
+ else {
+ obj[array[i]] = true;
+ }
+ }
+
+ return obj;
+};
+
+
+// Find the common unique items in two arrays
+
+exports.intersect = function (array1, array2, justFirst) {
+
+ if (!array1 || !array2) {
+ return [];
+ }
+
+ var common = [];
+ var hash = (Array.isArray(array1) ? exports.mapToObject(array1) : array1);
+ var found = {};
+ for (var i = 0, il = array2.length; i < il; ++i) {
+ if (hash[array2[i]] && !found[array2[i]]) {
+ if (justFirst) {
+ return array2[i];
+ }
+
+ common.push(array2[i]);
+ found[array2[i]] = true;
+ }
+ }
+
+ return (justFirst ? null : common);
+};
+
+
+// Test if the reference contains the values
+
+exports.contain = function (ref, values, options) {
+
+ /*
+ string -> string(s)
+ array -> item(s)
+ object -> key(s)
+ object -> object (key:value)
+ */
+
+ var valuePairs = null;
+ if (typeof ref === 'object' &&
+ typeof values === 'object' &&
+ !Array.isArray(ref) &&
+ !Array.isArray(values)) {
+
+ valuePairs = values;
+ values = Object.keys(values);
+ }
+ else {
+ values = [].concat(values);
+ }
+
+ options = options || {}; // deep, once, only, part
+
+ exports.assert(arguments.length >= 2, 'Insufficient arguments');
+ exports.assert(typeof ref === 'string' || typeof ref === 'object', 'Reference must be string or an object');
+ exports.assert(values.length, 'Values array cannot be empty');
+
+ var compare, compareFlags;
+ if (options.deep) {
+ compare = exports.deepEqual;
+
+ var hasOnly = options.hasOwnProperty('only'), hasPart = options.hasOwnProperty('part');
+
+ compareFlags = {
+ prototype: hasOnly ? options.only : hasPart ? !options.part : false,
+ part: hasOnly ? !options.only : hasPart ? options.part : true
+ };
+ }
+ else {
+ compare = function (a, b) {
+
+ return a === b;
+ };
+ }
+
+ var misses = false;
+ var matches = new Array(values.length);
+ for (var i = 0, il = matches.length; i < il; ++i) {
+ matches[i] = 0;
+ }
+
+ if (typeof ref === 'string') {
+ var pattern = '(';
+ for (i = 0, il = values.length; i < il; ++i) {
+ var value = values[i];
+ exports.assert(typeof value === 'string', 'Cannot compare string reference to non-string value');
+ pattern += (i ? '|' : '') + exports.escapeRegex(value);
+ }
+
+ var regex = new RegExp(pattern + ')', 'g');
+ var leftovers = ref.replace(regex, function ($0, $1) {
+
+ var index = values.indexOf($1);
+ ++matches[index];
+ return ''; // Remove from string
+ });
+
+ misses = !!leftovers;
+ }
+ else if (Array.isArray(ref)) {
+ for (i = 0, il = ref.length; i < il; ++i) {
+ for (var j = 0, jl = values.length, matched = false; j < jl && matched === false; ++j) {
+ matched = compare(values[j], ref[i], compareFlags) && j;
+ }
+
+ if (matched !== false) {
+ ++matches[matched];
+ }
+ else {
+ misses = true;
+ }
+ }
+ }
+ else {
+ var keys = Object.keys(ref);
+ for (i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ var pos = values.indexOf(key);
+ if (pos !== -1) {
+ if (valuePairs &&
+ !compare(valuePairs[key], ref[key], compareFlags)) {
+
+ return false;
+ }
+
+ ++matches[pos];
+ }
+ else {
+ misses = true;
+ }
+ }
+ }
+
+ var result = false;
+ for (i = 0, il = matches.length; i < il; ++i) {
+ result = result || !!matches[i];
+ if ((options.once && matches[i] > 1) ||
+ (!options.part && !matches[i])) {
+
+ return false;
+ }
+ }
+
+ if (options.only &&
+ misses) {
+
+ return false;
+ }
+
+ return result;
+};
+
+
+// Flatten array
+
+exports.flatten = function (array, target) {
+
+ var result = target || [];
+
+ for (var i = 0, il = array.length; i < il; ++i) {
+ if (Array.isArray(array[i])) {
+ exports.flatten(array[i], result);
+ }
+ else {
+ result.push(array[i]);
+ }
+ }
+
+ return result;
+};
+
+
+// Convert an object key chain string ('a.b.c') to reference (object[a][b][c])
+
+exports.reach = function (obj, chain, options) {
+
+ options = options || {};
+ if (typeof options === 'string') {
+ options = { separator: options };
+ }
+
+ var path = chain.split(options.separator || '.');
+ var ref = obj;
+ for (var i = 0, il = path.length; i < il; ++i) {
+ var key = path[i];
+ if (key[0] === '-' && Array.isArray(ref)) {
+ key = key.slice(1, key.length);
+ key = ref.length - key;
+ }
+
+ if (!ref ||
+ !ref.hasOwnProperty(key) ||
+ (typeof ref !== 'object' && options.functions === false)) { // Only object and function can have properties
+
+ exports.assert(!options.strict || i + 1 === il, 'Missing segment', key, 'in reach path ', chain);
+ exports.assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
+ ref = options.default;
+ break;
+ }
+
+ ref = ref[key];
+ }
+
+ return ref;
+};
+
+
+exports.reachTemplate = function (obj, template, options) {\r
+\r
+ return template.replace(/{([^}]+)}/g, function ($0, chain) {\r
+
+ var value = exports.reach(obj, chain, options);\r
+ return (value === undefined || value === null ? '' : value);\r
+ });
+};
+
+
+exports.formatStack = function (stack) {
+
+ var trace = [];
+ for (var i = 0, il = stack.length; i < il; ++i) {
+ var item = stack[i];
+ trace.push([item.getFileName(), item.getLineNumber(), item.getColumnNumber(), item.getFunctionName(), item.isConstructor()]);
+ }
+
+ return trace;
+};
+
+
+exports.formatTrace = function (trace) {
+
+ var display = [];
+
+ for (var i = 0, il = trace.length; i < il; ++i) {
+ var row = trace[i];
+ display.push((row[4] ? 'new ' : '') + row[3] + ' (' + row[0] + ':' + row[1] + ':' + row[2] + ')');
+ }
+
+ return display;
+};
+
+
+exports.callStack = function (slice) {
+
+ // http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
+
+ var v8 = Error.prepareStackTrace;
+ Error.prepareStackTrace = function (err, stack) {
+
+ return stack;
+ };
+
+ var capture = {};
+ Error.captureStackTrace(capture, arguments.callee); /*eslint no-caller:0 */
+ var stack = capture.stack;
+
+ Error.prepareStackTrace = v8;
+
+ var trace = exports.formatStack(stack);
+
+ if (slice) {
+ return trace.slice(slice);
+ }
+
+ return trace;
+};
+
+
+exports.displayStack = function (slice) {
+
+ var trace = exports.callStack(slice === undefined ? 1 : slice + 1);
+
+ return exports.formatTrace(trace);
+};
+
+
+exports.abortThrow = false;
+
+
+exports.abort = function (message, hideStack) {
+
+ if (process.env.NODE_ENV === 'test' || exports.abortThrow === true) {
+ throw new Error(message || 'Unknown error');
+ }
+
+ var stack = '';
+ if (!hideStack) {
+ stack = exports.displayStack(1).join('\n\t');
+ }
+ console.log('ABORT: ' + message + '\n\t' + stack);
+ process.exit(1);
+};
+
+
+exports.assert = function (condition /*, msg1, msg2, msg3 */) {
+
+ if (condition) {
+ return;
+ }
+
+ if (arguments.length === 2 && arguments[1] instanceof Error) {
+ throw arguments[1];
+ }
+
+ var msgs = [];
+ for (var i = 1, il = arguments.length; i < il; ++i) {
+ if (arguments[i] !== '') {
+ msgs.push(arguments[i]); // Avoids Array.slice arguments leak, allowing for V8 optimizations
+ }
+ }
+
+ msgs = msgs.map(function (msg) {
+
+ return typeof msg === 'string' ? msg : msg instanceof Error ? msg.message : exports.stringify(msg);
+ });
+ throw new Error(msgs.join(' ') || 'Unknown error');
+};
+
+
+exports.Timer = function () {
+
+ this.ts = 0;
+ this.reset();
+};
+
+
+exports.Timer.prototype.reset = function () {
+
+ this.ts = Date.now();
+};
+
+
+exports.Timer.prototype.elapsed = function () {
+
+ return Date.now() - this.ts;
+};
+
+
+exports.Bench = function () {
+
+ this.ts = 0;
+ this.reset();
+};
+
+
+exports.Bench.prototype.reset = function () {
+
+ this.ts = exports.Bench.now();
+};
+
+
+exports.Bench.prototype.elapsed = function () {
+
+ return exports.Bench.now() - this.ts;
+};
+
+
+exports.Bench.now = function () {
+
+ var ts = process.hrtime();
+ return (ts[0] * 1e3) + (ts[1] / 1e6);
+};
+
+
+// Escape string for Regex construction
+
+exports.escapeRegex = function (string) {
+
+ // Escape ^$.*+-?=!:|\/()[]{},
+ return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&');
+};
+
+
+// Base64url (RFC 4648) encode
+
+exports.base64urlEncode = function (value, encoding) {
+
+ var buf = (Buffer.isBuffer(value) ? value : new Buffer(value, encoding || 'binary'));
+ return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
+};
+
+
+// Base64url (RFC 4648) decode
+
+exports.base64urlDecode = function (value, encoding) {
+
+ if (value &&
+ !/^[\w\-]*$/.test(value)) {
+
+ return new Error('Invalid character');
+ }
+
+ try {
+ var buf = new Buffer(value, 'base64');
+ return (encoding === 'buffer' ? buf : buf.toString(encoding || 'binary'));
+ }
+ catch (err) {
+ return err;
+ }
+};
+
+
+// Escape attribute value for use in HTTP header
+
+exports.escapeHeaderAttribute = function (attribute) {
+
+ // Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, "
+
+ exports.assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')');
+
+ return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"'); // Escape quotes and slash
+};
+
+
+exports.escapeHtml = function (string) {
+
+ return Escape.escapeHtml(string);
+};
+
+
+exports.escapeJavaScript = function (string) {
+
+ return Escape.escapeJavaScript(string);
+};
+
+
+exports.nextTick = function (callback) {
+
+ return function () {
+
+ var args = arguments;
+ process.nextTick(function () {
+
+ callback.apply(null, args);
+ });
+ };
+};
+
+
+exports.once = function (method) {
+
+ if (method._hoekOnce) {
+ return method;
+ }
+
+ var once = false;
+ var wrapped = function () {
+
+ if (!once) {
+ once = true;
+ method.apply(null, arguments);
+ }
+ };
+
+ wrapped._hoekOnce = true;
+
+ return wrapped;
+};
+
+
+exports.isAbsolutePath = function (path, platform) {
+
+ if (!path) {
+ return false;
+ }
+
+ if (Path.isAbsolute) { // node >= 0.11
+ return Path.isAbsolute(path);
+ }
+
+ platform = platform || process.platform;
+
+ // Unix
+
+ if (platform !== 'win32') {
+ return path[0] === '/';
+ }
+
+ // Windows
+
+ return !!/^(?:[a-zA-Z]:[\\\/])|(?:[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/])/.test(path); // C:\ or \\something\something
+};
+
+
+exports.isInteger = function (value) {
+
+ return (typeof value === 'number' &&
+ parseFloat(value) === parseInt(value, 10) &&
+ !isNaN(value));
+};
+
+
+exports.ignore = function () { };
+
+
+exports.inherits = Util.inherits;
+
+
+exports.format = Util.format;
+
+
+exports.transform = function (source, transform, options) {
+
+ exports.assert(source === null || source === undefined || typeof source === 'object', 'Invalid source object: must be null, undefined, or an object');
+
+ var result = {};
+ var keys = Object.keys(transform);
+
+ for (var k = 0, kl = keys.length; k < kl; ++k) {
+ var key = keys[k];
+ var path = key.split('.');
+ var sourcePath = transform[key];
+
+ exports.assert(typeof sourcePath === 'string', 'All mappings must be "." delineated strings');
+
+ var segment;
+ var res = result;
+
+ while (path.length > 1) {
+ segment = path.shift();
+ if (!res[segment]) {
+ res[segment] = {};
+ }
+ res = res[segment];
+ }
+ segment = path.shift();
+ res[segment] = exports.reach(source, sourcePath, options);
+ }
+
+ return result;
+};
+
+
+exports.uniqueFilename = function (path, extension) {
+
+ if (extension) {
+ extension = extension[0] !== '.' ? '.' + extension : extension;
+ }
+ else {
+ extension = '';
+ }
+
+ path = Path.resolve(path);
+ var name = [Date.now(), process.pid, Crypto.randomBytes(8).toString('hex')].join('-') + extension;
+ return Path.join(path, name);
+};
+
+
+exports.stringify = function () {
+
+ try {
+ return JSON.stringify.apply(null, arguments);
+ }
+ catch (err) {
+ return '[Cannot display object: ' + err.message + ']';
+ }
+};
+
+
+exports.shallow = function (source) {
+
+ var target = {};
+ var keys = Object.keys(source);
+ for (var i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ target[key] = source[key];
+ }
+
+ return target;
+};
--- /dev/null
+{
+ "name": "hoek",
+ "description": "General purpose node utilities",
+ "version": "2.14.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/hapijs/hoek.git"
+ },
+ "main": "index",
+ "keywords": [
+ "utilities"
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "code": "1.x.x",
+ "lab": "5.x.x"
+ },
+ "scripts": {
+ "test": "make test-cov"
+ },
+ "license": "BSD-3-Clause",
+ "gitHead": "e80875a516f4b0eaa757ac4814c4510eebdcf589",
+ "bugs": {
+ "url": "https://github.com/hapijs/hoek/issues"
+ },
+ "homepage": "https://github.com/hapijs/hoek#readme",
+ "_id": "hoek@2.14.0",
+ "_shasum": "81211691f52a5a835ae49edbf1e89c9003476aa4",
+ "_from": "hoek@>=2.0.0 <3.0.0",
+ "_npmVersion": "2.9.0",
+ "_nodeVersion": "0.12.2",
+ "_npmUser": {
+ "name": "nlf",
+ "email": "quitlahok@gmail.com"
+ },
+ "dist": {
+ "shasum": "81211691f52a5a835ae49edbf1e89c9003476aa4",
+ "tarball": "http://registry.npmjs.org/hoek/-/hoek-2.14.0.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ },
+ {
+ "name": "wyatt",
+ "email": "wpreul@gmail.com"
+ },
+ {
+ "name": "nlf",
+ "email": "quitlahok@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/hoek/-/hoek-2.14.0.tgz"
+}
--- /dev/null
+// Load modules
+
+var Code = require('code');
+var Hoek = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('escapeJavaScript()', function () {
+
+ it('encodes / characters', function (done) {
+
+ var encoded = Hoek.escapeJavaScript('<script>alert(1)</script>');
+ expect(encoded).to.equal('\\x3cscript\\x3ealert\\x281\\x29\\x3c\\x2fscript\\x3e');
+ done();
+ });
+
+ it('encodes \' characters', function (done) {
+
+ var encoded = Hoek.escapeJavaScript('something(\'param\')');
+ expect(encoded).to.equal('something\\x28\\x27param\\x27\\x29');
+ done();
+ });
+
+ it('encodes large unicode characters with the correct padding', function (done) {
+
+ var encoded = Hoek.escapeJavaScript(String.fromCharCode(500) + String.fromCharCode(1000));
+ expect(encoded).to.equal('\\u0500\\u1000');
+ done();
+ });
+
+ it('doesn\'t throw an exception when passed null', function (done) {
+
+ var encoded = Hoek.escapeJavaScript(null);
+ expect(encoded).to.equal('');
+ done();
+ });
+});
+
+describe('escapeHtml()', function () {
+
+ it('encodes / characters', function (done) {
+
+ var encoded = Hoek.escapeHtml('<script>alert(1)</script>');
+ expect(encoded).to.equal('<script>alert(1)</script>');
+ done();
+ });
+
+ it('encodes < and > as named characters', function (done) {
+
+ var encoded = Hoek.escapeHtml('<script><>');
+ expect(encoded).to.equal('<script><>');
+ done();
+ });
+
+ it('encodes large unicode characters', function (done) {
+
+ var encoded = Hoek.escapeHtml(String.fromCharCode(500) + String.fromCharCode(1000));
+ expect(encoded).to.equal('ǴϨ');
+ done();
+ });
+
+ it('doesn\'t throw an exception when passed null', function (done) {
+
+ var encoded = Hoek.escapeHtml(null);
+ expect(encoded).to.equal('');
+ done();
+ });
+
+ it('encodes {} characters', function (done) {
+
+ var encoded = Hoek.escapeHtml('{}');
+ expect(encoded).to.equal('{}');
+ done();
+ });
+});
--- /dev/null
+// Load modules
+
+var Fs = require('fs');
+var Path = require('path');
+var Code = require('code');
+var Hoek = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+var nestedObj = {\r
+ v: [7, 8, 9],
+ w: /^something$/igm,
+ x: {\r
+ a: [1, 2, 3],
+ b: 123456,
+ c: new Date(),
+ d: /hi/igm,
+ e: /hello/\r
+ },
+ y: 'y',
+ z: new Date(1378775452757)\r
+};
+
+var dupsArray = [nestedObj, { z: 'z' }, nestedObj];
+var reducedDupsArray = [nestedObj, { z: 'z' }];
+
+describe('clone()', function () {\r
+
+ it('clones a nested object', function (done) {\r
+
+ var a = nestedObj;
+ var b = Hoek.clone(a);
+
+ expect(a).to.deep.equal(b);
+ expect(a.z.getTime()).to.equal(b.z.getTime());
+ done();\r
+ });
+
+ it('clones a null object', function (done) {\r
+
+ var b = Hoek.clone(null);
+
+ expect(b).to.equal(null);
+ done();\r
+ });
+
+ it('should not convert undefined properties to null', function (done) {\r
+
+ var obj = { something: undefined };
+ var b = Hoek.clone(obj);
+
+ expect(typeof b.something).to.equal('undefined');
+ done();\r
+ });
+
+ it('should not throw on circular reference', function (done) {\r
+
+ var a = {};
+ a.x = a;
+
+ var test = function () {\r
+
+ var b = Hoek.clone(a);\r
+ };
+
+ expect(test).to.not.throw();
+ done();\r
+ });
+
+ it('clones circular reference', function (done) {\r
+
+ var x = {\r
+ 'z': new Date()\r
+ };
+ x.y = x;
+
+ var b = Hoek.clone(x);
+ expect(Object.keys(b.y)).to.deep.equal(Object.keys(x));
+ expect(b.z).to.not.equal(x.z);
+ expect(b.y).to.not.equal(x.y);
+ expect(b.y.z).to.not.equal(x.y.z);
+ expect(b.y).to.equal(b);
+ expect(b.y.y.y.y).to.equal(b);
+ done();\r
+ });
+
+ it('clones an object with a null prototype', function (done) {\r
+
+ var obj = Object.create(null);
+ var b = Hoek.clone(obj);
+
+ expect(b).to.deep.equal(obj);
+ done();\r
+ });
+
+ it('clones deeply nested object', function (done) {\r
+
+ var a = {\r
+ x: {\r
+ y: {\r
+ a: [1, 2, 3],
+ b: 123456,
+ c: new Date(),
+ d: /hi/igm,
+ e: /hello/\r
+ }\r
+ }\r
+ };
+
+ var b = Hoek.clone(a);
+
+ expect(a).to.deep.equal(b);
+ expect(a.x.y.c.getTime()).to.equal(b.x.y.c.getTime());
+ done();\r
+ });
+
+ it('clones arrays', function (done) {\r
+
+ var a = [1, 2, 3];
+
+ var b = Hoek.clone(a);
+
+ expect(a).to.deep.equal(b);
+ done();\r
+ });
+
+ it('performs actual copy for shallow keys (no pass by reference)', function (done) {\r
+
+ var x = Hoek.clone(nestedObj);
+ var y = Hoek.clone(nestedObj);
+
+ // Date
+ expect(x.z).to.not.equal(nestedObj.z);
+ expect(x.z).to.not.equal(y.z);
+
+ // Regex
+ expect(x.w).to.not.equal(nestedObj.w);
+ expect(x.w).to.not.equal(y.w);
+
+ // Array
+ expect(x.v).to.not.equal(nestedObj.v);
+ expect(x.v).to.not.equal(y.v);
+
+ // Immutable(s)
+ x.y = 5;
+ expect(x.y).to.not.equal(nestedObj.y);
+ expect(x.y).to.not.equal(y.y);
+
+ done();\r
+ });
+
+ it('performs actual copy for deep keys (no pass by reference)', function (done) {\r
+
+ var x = Hoek.clone(nestedObj);
+ var y = Hoek.clone(nestedObj);
+
+ expect(x.x.c).to.not.equal(nestedObj.x.c);
+ expect(x.x.c).to.not.equal(y.x.c);
+
+ expect(x.x.c.getTime()).to.equal(nestedObj.x.c.getTime());
+ expect(x.x.c.getTime()).to.equal(y.x.c.getTime());
+ done();\r
+ });
+
+ it('copies functions with properties', function (done) {\r
+
+ var a = {\r
+ x: function () {\r
+\r
+ return 1;\r
+ },
+ y: {}\r
+ };
+ a.x.z = 'string in function';
+ a.x.v = function () {\r
+\r
+ return 2;\r
+ };
+ a.y.u = a.x;
+
+ var b = Hoek.clone(a);
+ expect(b.x()).to.equal(1);
+ expect(b.x.v()).to.equal(2);
+ expect(b.y.u).to.equal(b.x);
+ expect(b.x.z).to.equal('string in function');
+ done();\r
+ });
+
+ it('should copy a buffer', function (done) {
+
+ var tls = {
+ key: new Buffer([1, 2, 3, 4, 5]),
+ cert: new Buffer([1, 2, 3, 4, 5, 6, 10])\r
+ };
+
+ var copiedTls = Hoek.clone(tls);
+ expect(Buffer.isBuffer(copiedTls.key)).to.equal(true);
+ expect(JSON.stringify(copiedTls.key)).to.equal(JSON.stringify(tls.key));
+ expect(Buffer.isBuffer(copiedTls.cert)).to.equal(true);
+ expect(JSON.stringify(copiedTls.cert)).to.equal(JSON.stringify(tls.cert));
+ done();\r
+ });
+
+ it('clones an object with a prototype', function (done) {\r
+
+ var Obj = function () {\r
+
+ this.a = 5;\r
+ };
+
+ Obj.prototype.b = function () {
+
+ return 'c';
+ };
+
+ var a = new Obj();
+ var b = Hoek.clone(a);
+
+ expect(b.a).to.equal(5);
+ expect(b.b()).to.equal('c');
+ expect(a).to.deep.equal(b);
+ done();\r
+ });
+
+ it('reuses cloned Date object', function (done) {\r
+
+ var obj = {\r
+ a: new Date()\r
+ };
+
+ obj.b = obj.a;
+
+ var copy = Hoek.clone(obj);
+ expect(copy.a).to.equal(copy.b);
+ done();\r
+ });
+
+ it('shallow copies an object with a prototype and isImmutable flag', function (done) {\r
+
+ var Obj = function () {\r
+
+ this.value = 5;\r
+ };
+
+ Obj.prototype.b = function () {\r
+\r
+ return 'c';\r
+ };
+
+ Obj.prototype.isImmutable = true;
+
+ var obj = {\r
+ a: new Obj()\r
+ };
+
+ var copy = Hoek.clone(obj);
+
+ expect(obj.a.value).to.equal(5);
+ expect(copy.a.value).to.equal(5);
+ expect(copy.a.b()).to.equal('c');
+ expect(obj.a).to.equal(copy.a);
+ done();\r
+ });
+
+ it('clones an object with property getter without executing it', function (done) {\r
+
+ var obj = {};
+ var value = 1;
+ var execCount = 0;
+
+ Object.defineProperty(obj, 'test', {\r
+ enumerable: true,
+ configurable: true,
+ get: function () {\r
+
+ ++execCount;
+ return value;\r
+ }\r
+ });
+
+ var copy = Hoek.clone(obj);
+ expect(execCount).to.equal(0);
+ expect(copy.test).to.equal(1);
+ expect(execCount).to.equal(1);
+ done();\r
+ });
+
+ it('clones an object with property getter and setter', function (done) {\r
+
+ var obj = {\r
+ _test: 0\r
+ };
+
+ Object.defineProperty(obj, 'test', {\r
+ enumerable: true,
+ configurable: true,
+ get: function () {\r
+
+ return this._test;\r
+ },
+ set: function (value) {\r
+
+ this._test = value - 1;\r
+ }\r
+ });
+
+ var copy = Hoek.clone(obj);
+ expect(copy.test).to.equal(0);
+ copy.test = 5;
+ expect(copy.test).to.equal(4);
+ done();\r
+ });
+
+ it('clones an object with only property setter', function (done) {\r
+
+ var obj = {\r
+ _test: 0\r
+ };
+
+ Object.defineProperty(obj, 'test', {\r
+ enumerable: true,
+ configurable: true,
+ set: function (value) {\r
+
+ this._test = value - 1;\r
+ }\r
+ });
+
+ var copy = Hoek.clone(obj);
+ expect(copy._test).to.equal(0);
+ copy.test = 5;
+ expect(copy._test).to.equal(4);
+ done();\r
+ });
+
+ it('clones an object with non-enumerable properties', function (done) {\r
+
+ var obj = {\r
+ _test: 0\r
+ };
+
+ Object.defineProperty(obj, 'test', {\r
+ enumerable: false,
+ configurable: true,
+ set: function (value) {\r
+
+ this._test = value - 1;\r
+ }\r
+ });
+
+ var copy = Hoek.clone(obj);
+ expect(copy._test).to.equal(0);
+ copy.test = 5;
+ expect(copy._test).to.equal(4);
+ done();\r
+ });\r
+});
+
+describe('merge()', function () {\r
+
+ it('deep copies source items', function (done) {\r
+
+ var target = {\r
+ b: 3,
+ d: []\r
+ };
+
+ var source = {\r
+ c: {\r
+ d: 1\r
+ },
+ d: [{ e: 1 }]\r
+ };
+
+ Hoek.merge(target, source);
+ expect(target.c).to.not.equal(source.c);
+ expect(target.c).to.deep.equal(source.c);
+ expect(target.d).to.not.equal(source.d);
+ expect(target.d[0]).to.not.equal(source.d[0]);
+ expect(target.d).to.deep.equal(source.d);
+ done();\r
+ });
+
+ it('merges array over an object', function (done) {\r
+
+ var a = {\r
+ x: ['n', 'm']\r
+ };
+
+ var b = {\r
+ x: {\r
+ n: '1',
+ m: '2'\r
+ }\r
+ };
+
+ Hoek.merge(b, a);
+ expect(a.x[0]).to.equal('n');
+ expect(a.x.n).to.not.exist();
+ done();\r
+ });
+
+ it('merges object over an array', function (done) {\r
+
+ var a = {\r
+ x: ['n', 'm']\r
+ };
+
+ var b = {\r
+ x: {\r
+ n: '1',
+ m: '2'\r
+ }\r
+ };
+
+ Hoek.merge(a, b);
+ expect(a.x.n).to.equal('1');
+ expect(a.x[0]).to.not.exist();
+ done();\r
+ });
+
+ it('does not throw if source is null', function (done) {\r
+
+ var a = {};
+ var b = null;
+ var c = null;
+
+ expect(function () {\r
+
+ c = Hoek.merge(a, b);\r
+ }).to.not.throw();
+
+ expect(c).to.equal(a);
+ done();\r
+ });
+
+ it('does not throw if source is undefined', function (done) {\r
+
+ var a = {};
+ var b;
+ var c = null;
+
+ expect(function () {\r
+
+ c = Hoek.merge(a, b);\r
+ }).to.not.throw();
+
+ expect(c).to.equal(a);
+ done();\r
+ });
+
+ it('throws if source is not an object', function (done) {\r
+
+ expect(function () {\r
+
+ var a = {};
+ var b = 0;
+
+ Hoek.merge(a, b);\r
+ }).to.throw('Invalid source value: must be null, undefined, or an object');
+ done();\r
+ });
+
+ it('throws if target is not an object', function (done) {\r
+
+ expect(function () {\r
+
+ var a = 0;
+ var b = {};
+
+ Hoek.merge(a, b);\r
+ }).to.throw('Invalid target value: must be an object');
+ done();\r
+ });
+
+ it('throws if target is not an array and source is', function (done) {\r
+
+ expect(function () {\r
+
+ var a = {};
+ var b = [1, 2];
+
+ Hoek.merge(a, b);\r
+ }).to.throw('Cannot merge array onto an object');
+ done();\r
+ });
+
+ it('returns the same object when merging arrays', function (done) {\r
+
+ var a = [];
+ var b = [1, 2];
+
+ expect(Hoek.merge(a, b)).to.equal(a);
+ done();\r
+ });
+
+ it('combines an empty object with a non-empty object', function (done) {\r
+
+ var a = {};
+ var b = nestedObj;
+
+ var c = Hoek.merge(a, b);
+ expect(a).to.deep.equal(b);
+ expect(c).to.deep.equal(b);
+ done();\r
+ });
+
+ it('overrides values in target', function (done) {\r
+
+ var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' };
+ var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' };
+
+ var c = Hoek.merge(a, b);
+ expect(c.x).to.equal(null);
+ expect(c.y).to.equal(2);
+ expect(c.z).to.equal(4);
+ expect(c.v).to.equal(0);
+ expect(c.m).to.equal('123');
+ expect(c.t).to.deep.equal({ u: 6 });
+ done();\r
+ });
+
+ it('overrides values in target (flip)', function (done) {\r
+
+ var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' };
+ var b = { x: null, z: 4, v: 0, t: { u: 6 }, m: '123' };
+
+ var d = Hoek.merge(b, a);
+ expect(d.x).to.equal(1);
+ expect(d.y).to.equal(2);
+ expect(d.z).to.equal(3);
+ expect(d.v).to.equal(5);
+ expect(d.m).to.equal('abc');
+ expect(d.t).to.deep.equal('test');
+ done();\r
+ });
+
+ it('retains Date properties', function (done) {\r
+
+ var a = { x: new Date(1378776452757) };
+
+ var b = Hoek.merge({}, a);
+ expect(a.x.getTime()).to.equal(b.x.getTime());
+ done();\r
+ });
+
+ it('retains Date properties when merging keys', function (done) {\r
+
+ var a = { x: new Date(1378776452757) };
+
+ var b = Hoek.merge({ x: {} }, a);
+ expect(a.x.getTime()).to.equal(b.x.getTime());
+ done();\r
+ });
+
+ it('overrides Buffer', function (done) {\r
+
+ var a = { x: new Buffer('abc') };
+
+ var b = Hoek.merge({ x: {} }, a);
+ expect(a.x.toString()).to.equal('abc');
+ done();\r
+ });\r
+});
+
+describe('applyToDefaults()', function () {\r
+
+ var defaults = {\r
+ a: 1,
+ b: 2,
+ c: {\r
+ d: 3,
+ e: [5, 6]\r
+ },
+ f: 6,
+ g: 'test'\r
+ };
+
+ it('throws when target is null', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.applyToDefaults(null, {});\r
+ }).to.throw('Invalid defaults value: must be an object');
+ done();\r
+ });
+
+ it('returns null if options is false', function (done) {\r
+
+ var result = Hoek.applyToDefaults(defaults, false);
+ expect(result).to.equal(null);
+ done();\r
+ });
+
+ it('returns null if options is null', function (done) {\r
+
+ var result = Hoek.applyToDefaults(defaults, null);
+ expect(result).to.equal(null);
+ done();\r
+ });
+
+ it('returns null if options is undefined', function (done) {\r
+
+ var result = Hoek.applyToDefaults(defaults, undefined);
+ expect(result).to.equal(null);
+ done();\r
+ });
+
+ it('returns a copy of defaults if options is true', function (done) {\r
+
+ var result = Hoek.applyToDefaults(defaults, true);
+ expect(result).to.deep.equal(defaults);
+ done();\r
+ });
+
+ it('applies object to defaults', function (done) {\r
+
+ var obj = {\r
+ a: null,
+ c: {\r
+ e: [4]\r
+ },
+ f: 0,
+ g: {\r
+ h: 5\r
+ }\r
+ };
+
+ var result = Hoek.applyToDefaults(defaults, obj);
+ expect(result.c.e).to.deep.equal([4]);
+ expect(result.a).to.equal(1);
+ expect(result.b).to.equal(2);
+ expect(result.f).to.equal(0);
+ expect(result.g).to.deep.equal({ h: 5 });
+ done();\r
+ });
+
+ it('applies object to defaults with null', function (done) {\r
+
+ var obj = {\r
+ a: null,
+ c: {\r
+ e: [4]\r
+ },
+ f: 0,
+ g: {\r
+ h: 5\r
+ }\r
+ };
+
+ var result = Hoek.applyToDefaults(defaults, obj, true);
+ expect(result.c.e).to.deep.equal([4]);
+ expect(result.a).to.equal(null);
+ expect(result.b).to.equal(2);
+ expect(result.f).to.equal(0);
+ expect(result.g).to.deep.equal({ h: 5 });
+ done();\r
+ });\r
+});
+
+describe('cloneWithShallow()', function () {\r
+
+ it('deep clones except for listed keys', function (done) {\r
+
+ var source = {\r
+ a: {\r
+ b: 5\r
+ },
+ c: {\r
+ d: 6\r
+ }\r
+ };
+
+ var copy = Hoek.cloneWithShallow(source, ['c']);
+ expect(copy).to.deep.equal(source);
+ expect(copy).to.not.equal(source);
+ expect(copy.a).to.not.equal(source.a);
+ expect(copy.b).to.equal(source.b);
+ done();\r
+ });
+
+ it('returns immutable value', function (done) {\r
+
+ expect(Hoek.cloneWithShallow(5)).to.equal(5);
+ done();\r
+ });
+
+ it('returns null value', function (done) {\r
+
+ expect(Hoek.cloneWithShallow(null)).to.equal(null);
+ done();\r
+ });
+
+ it('returns undefined value', function (done) {\r
+
+ expect(Hoek.cloneWithShallow(undefined)).to.equal(undefined);
+ done();\r
+ });
+
+ it('deep clones except for listed keys (including missing keys)', function (done) {\r
+
+ var source = {\r
+ a: {\r
+ b: 5\r
+ },
+ c: {\r
+ d: 6\r
+ }\r
+ };
+
+ var copy = Hoek.cloneWithShallow(source, ['c', 'v']);
+ expect(copy).to.deep.equal(source);
+ expect(copy).to.not.equal(source);
+ expect(copy.a).to.not.equal(source.a);
+ expect(copy.b).to.equal(source.b);
+ done();\r
+ });\r
+});
+
+describe('applyToDefaultsWithShallow()', function () {\r
+
+ it('shallow copies the listed keys from options without merging', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 5,
+ e: 3\r
+ },
+ c: {\r
+ d: 7,
+ g: 1\r
+ }\r
+ };
+
+ var options = {\r
+ a: {\r
+ b: 4\r
+ },
+ c: {\r
+ d: 6,
+ f: 7\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['a']);
+ expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: 1, f: 7 } });
+ expect(merged.a).to.equal(options.a);
+ expect(merged.a).to.not.equal(defaults.a);
+ expect(merged.c).to.not.equal(options.c);
+ expect(merged.c).to.not.equal(defaults.c);
+ done();\r
+ });
+
+ it('shallow copies the nested keys (override)', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 5\r
+ },
+ c: {\r
+ d: 7,
+ g: 1\r
+ }\r
+ };
+
+ var options = {\r
+ a: {\r
+ b: 4\r
+ },
+ c: {\r
+ d: 6,
+ g: {\r
+ h: 8\r
+ }\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
+ expect(merged).to.deep.equal({ a: { b: 4 }, c: { d: 6, g: { h: 8 } } });
+ expect(merged.c.g).to.equal(options.c.g);
+ done();\r
+ });
+
+ it('shallow copies the nested keys (missing)', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 5\r
+ }\r
+ };
+
+ var options = {\r
+ a: {\r
+ b: 4\r
+ },
+ c: {\r
+ g: {\r
+ h: 8\r
+ }\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
+ expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } });
+ expect(merged.c.g).to.equal(options.c.g);
+ done();\r
+ });
+
+ it('shallow copies the nested keys (override)', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 5\r
+ },
+ c: {\r
+ g: {\r
+ d: 7\r
+ }\r
+ }\r
+ };
+
+ var options = {\r
+ a: {\r
+ b: 4\r
+ },
+ c: {\r
+ g: {\r
+ h: 8\r
+ }\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g']);
+ expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { h: 8 } } });
+ expect(merged.c.g).to.equal(options.c.g);
+ done();\r
+ });
+
+ it('shallow copies the nested keys (deeper)', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 5\r
+ }\r
+ };
+
+ var options = {\r
+ a: {\r
+ b: 4\r
+ },
+ c: {\r
+ g: {\r
+ r: {\r
+ h: 8\r
+ }\r
+ }\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['c.g.r']);
+ expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } });
+ expect(merged.c.g.r).to.equal(options.c.g.r);
+ done();\r
+ });
+
+ it('shallow copies the nested keys (not present)', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 5\r
+ }\r
+ };
+
+ var options = {\r
+ a: {\r
+ b: 4\r
+ },
+ c: {\r
+ g: {\r
+ r: {\r
+ h: 8\r
+ }\r
+ }\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, options, ['x.y']);
+ expect(merged).to.deep.equal({ a: { b: 4 }, c: { g: { r: { h: 8 } } } });
+ done();\r
+ });
+
+ it('shallow copies the listed keys in the defaults', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 1\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, {}, ['a']);
+ expect(merged.a).to.equal(defaults.a);
+ done();\r
+ });
+
+ it('shallow copies the listed keys in the defaults (true)', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 1\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, true, ['a']);
+ expect(merged.a).to.equal(defaults.a);
+ done();\r
+ });
+
+ it('returns null on false', function (done) {\r
+
+ var defaults = {\r
+ a: {\r
+ b: 1\r
+ }\r
+ };
+
+ var merged = Hoek.applyToDefaultsWithShallow(defaults, false, ['a']);
+ expect(merged).to.equal(null);
+ done();\r
+ });
+
+ it('throws on missing defaults', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.applyToDefaultsWithShallow(null, {}, ['a']);\r
+ }).to.throw('Invalid defaults value: must be an object');
+ done();\r
+ });
+
+ it('throws on invalid defaults', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.applyToDefaultsWithShallow('abc', {}, ['a']);\r
+ }).to.throw('Invalid defaults value: must be an object');
+ done();\r
+ });
+
+ it('throws on invalid options', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.applyToDefaultsWithShallow({}, 'abc', ['a']);\r
+ }).to.throw('Invalid options value: must be true, falsy or an object');
+ done();\r
+ });
+
+ it('throws on missing keys', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.applyToDefaultsWithShallow({}, true);\r
+ }).to.throw('Invalid keys');
+ done();\r
+ });
+
+ it('throws on invalid keys', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.applyToDefaultsWithShallow({}, true, 'a');\r
+ }).to.throw('Invalid keys');
+ done();\r
+ });\r
+});
+
+describe('deepEqual()', function () {\r
+
+ it('compares simple values', function (done) {\r
+
+ expect(Hoek.deepEqual('x', 'x')).to.be.true();
+ expect(Hoek.deepEqual('x', 'y')).to.be.false();
+ expect(Hoek.deepEqual('x1', 'x')).to.be.false();
+ expect(Hoek.deepEqual(-0, +0)).to.be.false();
+ expect(Hoek.deepEqual(-0, -0)).to.be.true();
+ expect(Hoek.deepEqual(+0, +0)).to.be.true();
+ expect(Hoek.deepEqual(+0, -0)).to.be.false();
+ expect(Hoek.deepEqual(1, 1)).to.be.true();
+ expect(Hoek.deepEqual(0, 0)).to.be.true();
+ expect(Hoek.deepEqual(-1, 1)).to.be.false();
+ expect(Hoek.deepEqual(NaN, 0)).to.be.false();
+ expect(Hoek.deepEqual(NaN, NaN)).to.be.true();
+ done();\r
+ });
+
+ it('compares different types', function (done) {\r
+
+ expect(Hoek.deepEqual([], 5)).to.be.false();
+ expect(Hoek.deepEqual(5, [])).to.be.false();
+ expect(Hoek.deepEqual({}, null)).to.be.false();
+ expect(Hoek.deepEqual(null, {})).to.be.false();
+ expect(Hoek.deepEqual('abc', {})).to.be.false();
+ expect(Hoek.deepEqual({}, 'abc')).to.be.false();
+ done();\r
+ });
+
+ it('compares empty structures', function (done) {\r
+
+ expect(Hoek.deepEqual([], [])).to.be.true();
+ expect(Hoek.deepEqual({}, {})).to.be.true();
+ expect(Hoek.deepEqual([], {})).to.be.false();
+ done();\r
+ });
+
+ it('compares empty arguments object', function (done) {\r
+
+ var compare = function () {\r
+
+ expect(Hoek.deepEqual([], arguments)).to.be.false();\r
+ };
+
+ compare();
+ done();\r
+ });
+
+ it('compares empty arguments objects', function (done) {\r
+
+ var compare = function () {\r
+
+ var arg1 = arguments;
+
+ var inner = function () {\r
+
+ expect(Hoek.deepEqual(arg1, arguments)).to.be.false(); // callee is not the same
+ };
+
+ inner();\r
+ };
+
+ compare();
+ done();\r
+ });
+
+ it('compares dates', function (done) {\r
+
+ expect(Hoek.deepEqual(new Date(2015, 1, 1), new Date(2015, 1, 1))).to.be.true();
+ expect(Hoek.deepEqual(new Date(100), new Date(101))).to.be.false();
+ expect(Hoek.deepEqual(new Date(), {})).to.be.false();
+ done();\r
+ });
+
+ it('compares regular expressions', function (done) {\r
+
+ expect(Hoek.deepEqual(/\s/, new RegExp('\\\s'))).to.be.true();
+ expect(Hoek.deepEqual(/\s/g, /\s/g)).to.be.true();
+ expect(Hoek.deepEqual(/a/, {})).to.be.false();
+ expect(Hoek.deepEqual(/\s/g, /\s/i)).to.be.false();
+ expect(Hoek.deepEqual(/a/g, /b/g)).to.be.false();
+ done();\r
+ });
+
+ it('compares arrays', function (done) {\r
+
+ expect(Hoek.deepEqual([[1]], [[1]])).to.be.true();
+ expect(Hoek.deepEqual([1, 2, 3], [1, 2, 3])).to.be.true();
+ expect(Hoek.deepEqual([1, 2, 3], [1, 3, 2])).to.be.false();
+ expect(Hoek.deepEqual([1, 2, 3], [1, 2])).to.be.false();
+ expect(Hoek.deepEqual([1], [1])).to.be.true();
+ done();\r
+ });
+
+ it('compares buffers', function (done) {\r
+
+ expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2, 3]))).to.be.true();
+ expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 3, 2]))).to.be.false();
+ expect(Hoek.deepEqual(new Buffer([1, 2, 3]), new Buffer([1, 2]))).to.be.false();
+ expect(Hoek.deepEqual(new Buffer([1, 2, 3]), {})).to.be.false();
+ expect(Hoek.deepEqual(new Buffer([1, 2, 3]), [1, 2, 3])).to.be.false();
+ done();\r
+ });
+
+ it('compares objects', function (done) {\r
+
+ expect(Hoek.deepEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).to.be.true();
+ expect(Hoek.deepEqual({ foo: 'bar' }, { foo: 'baz' })).to.be.false();
+ expect(Hoek.deepEqual({ foo: { bar: 'foo' } }, { foo: { bar: 'baz' } })).to.be.false();
+ done();\r
+ });
+
+ it('handles circular dependency', function (done) {\r
+
+ var a = {};
+ a.x = a;
+
+ var b = Hoek.clone(a);
+ expect(Hoek.deepEqual(a, b)).to.be.true();
+ done();\r
+ });
+
+ it('compares an object with property getter without executing it', function (done) {\r
+
+ var obj = {};
+ var value = 1;
+ var execCount = 0;
+
+ Object.defineProperty(obj, 'test', {\r
+ enumerable: true,
+ configurable: true,
+ get: function () {\r
+
+ ++execCount;
+ return value;\r
+ }\r
+ });
+
+ var copy = Hoek.clone(obj);
+ expect(Hoek.deepEqual(obj, copy)).to.be.true();
+ expect(execCount).to.equal(0);
+ expect(copy.test).to.equal(1);
+ expect(execCount).to.equal(1);
+ done();\r
+ });
+
+ it('compares objects with property getters', function (done) {\r
+
+ var obj = {};
+ Object.defineProperty(obj, 'test', {\r
+ enumerable: true,
+ configurable: true,
+ get: function () {
+
+ return 1;
+ }
+ });
+
+ var ref = {};
+ Object.defineProperty(ref, 'test', {\r
+ enumerable: true,
+ configurable: true,
+ get: function () {
+
+ return 2;
+ }
+ });
+
+ expect(Hoek.deepEqual(obj, ref)).to.be.false();
+ done();\r
+ });
+
+ it('compares object prototypes', function (done) {\r
+
+ var Obj = function () {\r
+
+ this.a = 5;\r
+ };
+
+ Obj.prototype.b = function () {
+
+ return this.a;
+ };
+
+ var Ref = function () {\r
+
+ this.a = 5;\r
+ };
+
+ Ref.prototype.b = function () {
+
+ return this.a;
+ };
+
+ expect(Hoek.deepEqual(new Obj(), new Ref())).to.be.false();
+ expect(Hoek.deepEqual(new Obj(), new Obj())).to.be.true();
+ expect(Hoek.deepEqual(new Ref(), new Ref())).to.be.true();
+ done();\r
+ });
+
+ it('compares plain objects', function (done) {\r
+
+ var a = Object.create(null);
+ var b = Object.create(null);
+
+ a.b = 'c';
+ b.b = 'c';
+
+ expect(Hoek.deepEqual(a, b)).to.be.true();
+ expect(Hoek.deepEqual(a, { b: 'c' })).to.be.false();
+ done();\r
+ });
+
+ it('compares an object with an empty object', function (done) {\r
+
+ var a = { a: 1, b: 2 };
+
+ expect(Hoek.deepEqual({}, a)).to.be.false();
+ expect(Hoek.deepEqual(a, {})).to.be.false();
+ done();\r
+ });
+
+ it('compares an object ignoring the prototype', function (done) {\r
+
+ var a = Object.create(null);
+ var b = {};
+
+ expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true();
+ done();\r
+ });
+
+ it('compares an object ignoring the prototype recursively', function (done) {\r
+
+ var a = [Object.create(null)];
+ var b = [{}];
+
+ expect(Hoek.deepEqual(a, b, { prototype: false })).to.be.true();
+ done();\r
+ });\r
+});
+
+describe('unique()', function () {\r
+
+ it('ensures uniqueness within array of objects based on subkey', function (done) {\r
+
+ var a = Hoek.unique(dupsArray, 'x');
+ expect(a).to.deep.equal(reducedDupsArray);
+ done();\r
+ });
+
+ it('removes duplicated without key', function (done) {\r
+
+ expect(Hoek.unique([1, 2, 3, 4, 2, 1, 5])).to.deep.equal([1, 2, 3, 4, 5]);
+ done();\r
+ });\r
+});
+
+describe('mapToObject()', function () {\r
+
+ it('returns null on null array', function (done) {\r
+
+ var a = Hoek.mapToObject(null);
+ expect(a).to.equal(null);
+ done();\r
+ });
+
+ it('converts basic array to existential object', function (done) {\r
+
+ var keys = [1, 2, 3, 4];
+ var a = Hoek.mapToObject(keys);
+ for (var i in keys) {\r
+ expect(a[keys[i]]).to.equal(true);\r
+ }
+ done();\r
+ });
+
+ it('converts array of objects to existential object', function (done) {\r
+
+ var keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }];
+ var subkey = 'x';
+ var a = Hoek.mapToObject(keys, subkey);
+ expect(a).to.deep.equal({ 1: true, 2: true, 3: true });
+ done();\r
+ });\r
+});
+
+describe('intersect()', function () {\r
+
+ it('returns the common objects of two arrays', function (done) {\r
+
+ var array1 = [1, 2, 3, 4, 4, 5, 5];
+ var array2 = [5, 4, 5, 6, 7];
+ var common = Hoek.intersect(array1, array2);
+ expect(common.length).to.equal(2);
+ done();\r
+ });
+
+ it('returns just the first common object of two arrays', function (done) {\r
+
+ var array1 = [1, 2, 3, 4, 4, 5, 5];
+ var array2 = [5, 4, 5, 6, 7];
+ var common = Hoek.intersect(array1, array2, true);
+ expect(common).to.equal(5);
+ done();\r
+ });
+
+ it('returns null when no common and returning just the first common object of two arrays', function (done) {\r
+
+ var array1 = [1, 2, 3, 4, 4, 5, 5];
+ var array2 = [6, 7];
+ var common = Hoek.intersect(array1, array2, true);
+ expect(common).to.equal(null);
+ done();\r
+ });
+
+ it('returns an empty array if either input is null', function (done) {\r
+
+ expect(Hoek.intersect([1], null).length).to.equal(0);
+ expect(Hoek.intersect(null, [1]).length).to.equal(0);
+ done();\r
+ });
+
+ it('returns the common objects of object and array', function (done) {\r
+
+ var array1 = [1, 2, 3, 4, 4, 5, 5];
+ var array2 = [5, 4, 5, 6, 7];
+ var common = Hoek.intersect(Hoek.mapToObject(array1), array2);
+ expect(common.length).to.equal(2);
+ done();\r
+ });\r
+});
+
+describe('contain()', function () {\r
+
+ it('tests strings', function (done) {\r
+
+ expect(Hoek.contain('abc', 'ab')).to.be.true();
+ expect(Hoek.contain('abc', 'abc', { only: true })).to.be.true();
+ expect(Hoek.contain('aaa', 'a', { only: true })).to.be.true();
+ expect(Hoek.contain('abc', 'b', { once: true })).to.be.true();
+ expect(Hoek.contain('abc', ['a', 'c'])).to.be.true();
+ expect(Hoek.contain('abc', ['a', 'd'], { part: true })).to.be.true();
+
+ expect(Hoek.contain('abc', 'ac')).to.be.false();
+ expect(Hoek.contain('abcd', 'abc', { only: true })).to.be.false();
+ expect(Hoek.contain('aab', 'a', { only: true })).to.be.false();
+ expect(Hoek.contain('abb', 'b', { once: true })).to.be.false();
+ expect(Hoek.contain('abc', ['a', 'd'])).to.be.false();
+ expect(Hoek.contain('abc', ['ab', 'bc'])).to.be.false(); // Overlapping values not supported
+ done();\r
+ });
+
+ it('tests arrays', function (done) {\r
+
+ expect(Hoek.contain([1, 2, 3], 1)).to.be.true();
+ expect(Hoek.contain([{ a: 1 }], { a: 1 }, { deep: true })).to.be.true();
+ expect(Hoek.contain([1, 2, 3], [1, 2])).to.be.true();
+ expect(Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true })).to.be.true();
+ expect(Hoek.contain([1, 1, 2], [1, 2], { only: true })).to.be.true();
+ expect(Hoek.contain([1, 2], [1, 2], { once: true })).to.be.true();
+ expect(Hoek.contain([1, 2, 3], [1, 4], { part: true })).to.be.true();
+ expect(Hoek.contain([[1], [2]], [[1]], { deep: true })).to.be.true();
+
+ expect(Hoek.contain([1, 2, 3], 4)).to.be.false();
+ expect(Hoek.contain([{ a: 1 }], { a: 2 }, { deep: true })).to.be.false();
+ expect(Hoek.contain([{ a: 1 }], { a: 1 })).to.be.false();
+ expect(Hoek.contain([1, 2, 3], [4, 5])).to.be.false();
+ expect(Hoek.contain([[3], [2]], [[1]])).to.be.false();
+ expect(Hoek.contain([[1], [2]], [[1]])).to.be.false();
+ expect(Hoek.contain([{ a: 1 }], [{ a: 2 }], { deep: true })).to.be.false();
+ expect(Hoek.contain([1, 3, 2], [1, 2], { only: true })).to.be.false();
+ expect(Hoek.contain([1, 2, 2], [1, 2], { once: true })).to.be.false();
+ expect(Hoek.contain([0, 2, 3], [1, 4], { part: true })).to.be.false();
+ done();\r
+ });
+
+ it('tests objects', function (done) {\r
+
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'a')).to.be.true();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'c'])).to.be.true();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'b', 'c'], { only: true })).to.be.true();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1 })).to.be.true();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, c: 3 })).to.be.true();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true })).to.be.true();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, { only: true })).to.be.true();
+ expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] }, { deep: true })).to.be.true();
+ expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true })).to.be.true();
+ expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: true })).to.be.true();
+ expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, part: false })).to.be.false();
+ expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: true })).to.be.false();
+ expect(Hoek.contain({ a: [{ b: 1 }, { c: 2 }, { d: 3, e: 4 }] }, { a: [{ b: 1 }, { d: 3 }] }, { deep: true, only: false })).to.be.true();
+
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'd')).to.be.false();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'd'])).to.be.false();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3, d: 4 }, ['a', 'b', 'c'], { only: true })).to.be.false();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2 })).to.be.false();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2, b: 2 }, { part: true })).to.be.false(); // part does not ignore bad value
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 3 })).to.be.false();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 })).to.be.false();
+ expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 }, { only: true })).to.be.false();
+ expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] })).to.be.false();
+ expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } })).to.be.false();
+ expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true })).to.be.true();
+ expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: true })).to.be.false();
+ expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, only: false })).to.be.true();
+ expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: true })).to.be.true();
+ expect(Hoek.contain({ a: { b: { c: 1, d: 2 } } }, { a: { b: { c: 1 } } }, { deep: true, part: false })).to.be.false();
+
+ // Getter check
+ var Foo = function (bar) {
+
+ this.bar = bar;\r
+ };
+
+ Object.defineProperty(Foo.prototype, 'baz', {\r
+ enumerable: true,
+ get: function () {
+
+ return this.bar;
+ }
+ });
+
+ expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true })).to.be.true();
+ expect(Hoek.contain({ a: new Foo('b') }, { a: new Foo('b') }, { deep: true, part: true })).to.be.true();
+ expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true })).to.be.true();
+ expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, only: true })).to.be.false();
+ expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: false })).to.be.false();
+ expect(Hoek.contain({ a: new Foo('b') }, { a: { baz: 'b' } }, { deep: true, part: true })).to.be.true();
+
+ done();\r
+ });\r
+});
+
+describe('flatten()', function () {\r
+
+ it('returns a flat array', function (done) {\r
+
+ var result = Hoek.flatten([1, 2, [3, 4, [5, 6], [7], 8], [9], [10, [11, 12]], 13]);
+ expect(result.length).to.equal(13);
+ expect(result).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
+ done();\r
+ });\r
+});
+
+describe('reach()', function () {\r
+
+ var obj = {\r
+ a: {\r
+ b: {\r
+ c: {\r
+ d: 1,
+ e: 2\r
+ },
+ f: 'hello'\r
+ },
+ g: {\r
+ h: 3\r
+ }\r
+ },
+ i: function () { },
+ j: null,
+ k: [4, 8, 9, 1]\r
+ };
+
+ obj.i.x = 5;
+
+ it('returns first value of array', function (done) {\r
+
+ expect(Hoek.reach(obj, 'k.0')).to.equal(4);
+ done();\r
+ });
+
+ it('returns last value of array using negative index', function (done) {\r
+
+ expect(Hoek.reach(obj, 'k.-2')).to.equal(9);
+ done();\r
+ });
+
+ it('returns a valid member', function (done) {\r
+
+ expect(Hoek.reach(obj, 'a.b.c.d')).to.equal(1);
+ done();\r
+ });
+
+ it('returns a valid member with separator override', function (done) {\r
+
+ expect(Hoek.reach(obj, 'a/b/c/d', '/')).to.equal(1);
+ done();\r
+ });
+
+ it('returns undefined on null object', function (done) {\r
+
+ expect(Hoek.reach(null, 'a.b.c.d')).to.equal(undefined);
+ done();\r
+ });
+
+ it('returns undefined on missing object member', function (done) {\r
+
+ expect(Hoek.reach(obj, 'a.b.c.d.x')).to.equal(undefined);
+ done();\r
+ });
+
+ it('returns undefined on missing function member', function (done) {\r
+
+ expect(Hoek.reach(obj, 'i.y', { functions: true })).to.equal(undefined);
+ done();\r
+ });
+
+ it('throws on missing member in strict mode', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.reach(obj, 'a.b.c.o.x', { strict: true });\r
+ }).to.throw('Missing segment o in reach path a.b.c.o.x');
+
+ done();\r
+ });
+
+ it('returns undefined on invalid member', function (done) {\r
+
+ expect(Hoek.reach(obj, 'a.b.c.d-.x')).to.equal(undefined);
+ done();\r
+ });
+
+ it('returns function member', function (done) {\r
+
+ expect(typeof Hoek.reach(obj, 'i')).to.equal('function');
+ done();\r
+ });
+
+ it('returns function property', function (done) {\r
+
+ expect(Hoek.reach(obj, 'i.x')).to.equal(5);
+ done();\r
+ });
+
+ it('returns null', function (done) {\r
+
+ expect(Hoek.reach(obj, 'j')).to.equal(null);
+ done();\r
+ });
+
+ it('throws on function property when functions not allowed', function (done) {\r
+
+ expect(function () {\r
+
+ Hoek.reach(obj, 'i.x', { functions: false });\r
+ }).to.throw('Invalid segment x in reach path i.x');
+
+ done();\r
+ });
+
+ it('will return a default value if property is not found', function (done) {\r
+
+ expect(Hoek.reach(obj, 'a.b.q', { default: 'defaultValue' })).to.equal('defaultValue');
+ done();\r
+ });
+
+ it('will return a default value if path is not found', function (done) {\r
+
+ expect(Hoek.reach(obj, 'q', { default: 'defaultValue' })).to.equal('defaultValue');
+ done();\r
+ });
+
+ it('allows a falsey value to be used as the default value', function (done) {\r
+
+ expect(Hoek.reach(obj, 'q', { default: '' })).to.equal('');
+ done();\r
+ });\r
+});
+
+describe('reachTemplate()', function () {\r
+
+ it('applies object to template', function (done) {\r
+
+ var obj = {\r
+ a: {\r
+ b: {\r
+ c: {\r
+ d: 1\r
+ }\r
+ }\r
+ },
+ j: null,
+ k: [4, 8, 9, 1]\r
+ };
+
+ var template = '{k.0}:{k.-2}:{a.b.c.d}:{x.y}:{j}';
+
+ expect(Hoek.reachTemplate(obj, template)).to.equal('4:9:1::');
+ done();\r
+ });\r
+
+ it('applies object to template (options)', function (done) {\r
+
+ var obj = {\r
+ a: {\r
+ b: {\r
+ c: {\r
+ d: 1\r
+ }\r
+ }\r
+ },
+ j: null,
+ k: [4, 8, 9, 1]\r
+ };
+
+ var template = '{k/0}:{k/-2}:{a/b/c/d}:{x/y}:{j}';
+
+ expect(Hoek.reachTemplate(obj, template, '/')).to.equal('4:9:1::');
+ done();\r
+ });\r
+});
+
+describe('callStack()', function () {\r
+
+ it('returns the full call stack', function (done) {\r
+
+ var stack = Hoek.callStack();
+ expect(stack[0][0]).to.contain('index.js');
+ expect(stack[0][2]).to.equal(26);
+ done();\r
+ });\r
+});
+
+describe('displayStack ()', function () {\r
+
+ it('returns the full call stack for display', function (done) {\r
+
+ var stack = Hoek.displayStack();
+ expect(stack[0]).to.contain(Path.normalize('/test/index.js') + ':');
+ done();\r
+ });
+
+ it('includes constructor functions correctly', function (done) {\r
+
+ var Something = function (next) {\r
+
+ next();\r
+ };
+
+ var something = new Something(function () {\r
+
+ var stack = Hoek.displayStack();
+ expect(stack[1]).to.contain('new Something');
+ done();\r
+ });\r
+ });\r
+});
+
+describe('abort()', function () {\r
+
+ it('exits process when not in test mode', function (done) {\r
+
+ var env = process.env.NODE_ENV;
+ var write = process.stdout.write;
+ var exit = process.exit;
+
+ process.env.NODE_ENV = 'nottatest';
+ process.stdout.write = function () { };
+ process.exit = function (state) {\r
+
+ process.exit = exit;
+ process.env.NODE_ENV = env;
+ process.stdout.write = write;
+
+ expect(state).to.equal(1);
+ done();\r
+ };
+
+ Hoek.abort('Boom');\r
+ });
+
+ it('throws when not in test mode and abortThrow is true', function (done) {\r
+
+ var env = process.env.NODE_ENV;
+ process.env.NODE_ENV = 'nottatest';
+ Hoek.abortThrow = true;
+
+ var fn = function () {\r
+
+ Hoek.abort('my error message');\r
+ };
+
+ expect(fn).to.throw('my error message');
+ Hoek.abortThrow = false;
+ process.env.NODE_ENV = env;
+
+ done();\r
+ });
+
+ it('respects hideStack argument', function (done) {\r
+
+ var env = process.env.NODE_ENV;
+ var write = process.stdout.write;
+ var exit = process.exit;
+ var output = '';
+
+ process.exit = function () { };
+ process.env.NODE_ENV = '';
+ process.stdout.write = function (message) {\r
+
+ output = message;\r
+ };
+
+ Hoek.abort('my error message', true);
+
+ process.env.NODE_ENV = env;
+ process.stdout.write = write;
+ process.exit = exit;
+
+ expect(output).to.equal('ABORT: my error message\n\t\n');
+
+ done();\r
+ });
+
+ it('throws in test mode', function (done) {\r
+
+ var env = process.env.NODE_ENV;
+ process.env.NODE_ENV = 'test';
+
+ expect(function () {\r
+
+ Hoek.abort('my error message', true);\r
+ }).to.throw('my error message');
+
+ process.env.NODE_ENV = env;
+ done();\r
+ });
+
+ it('throws in test mode with default message', function (done) {\r
+
+ var env = process.env.NODE_ENV;
+ process.env.NODE_ENV = 'test';
+
+ expect(function () {\r
+
+ Hoek.abort('', true);\r
+ }).to.throw('Unknown error');
+
+ process.env.NODE_ENV = env;
+ done();\r
+ });
+
+ it('defaults to showing stack', function (done) {\r
+
+ var env = process.env.NODE_ENV;
+ var write = process.stdout.write;
+ var exit = process.exit;
+ var output = '';
+
+ process.exit = function () { };
+ process.env.NODE_ENV = '';
+ process.stdout.write = function (message) {\r
+
+ output = message;\r
+ };
+
+ Hoek.abort('my error message');
+
+ process.env.NODE_ENV = env;
+ process.stdout.write = write;
+ process.exit = exit;
+
+ expect(output).to.contain('index.js');
+
+ done();\r
+ });\r
+});
+
+describe('assert()', function () {\r
+
+ it('throws an Error when using assert in a test', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false, 'my error message');\r
+ };
+
+ expect(fn).to.throw('my error message');
+ done();\r
+ });
+
+ it('throws an Error when using assert in a test with no message', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false);\r
+ };
+
+ expect(fn).to.throw('Unknown error');
+ done();\r
+ });
+
+ it('throws an Error when using assert in a test with multipart message', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false, 'This', 'is', 'my message');\r
+ };
+
+ expect(fn).to.throw('This is my message');
+ done();\r
+ });
+
+ it('throws an Error when using assert in a test with multipart message (empty)', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false, 'This', 'is', '', 'my message');\r
+ };
+
+ expect(fn).to.throw('This is my message');
+ done();\r
+ });
+
+ it('throws an Error when using assert in a test with object message', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false, 'This', 'is', { spinal: 'tap' });\r
+ };
+
+ expect(fn).to.throw('This is {"spinal":"tap"}');
+ done();\r
+ });
+
+ it('throws an Error when using assert in a test with multipart string and error messages', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false, 'This', 'is', new Error('spinal'), new Error('tap'));\r
+ };
+
+ expect(fn).to.throw('This is spinal tap');
+ done();\r
+ });
+
+ it('throws an Error when using assert in a test with error object message', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.assert(false, new Error('This is spinal tap'));\r
+ };
+
+ expect(fn).to.throw('This is spinal tap');
+ done();\r
+ });
+
+ it('throws the same Error that is passed to it if there is only one error passed', function (done) {\r
+
+ var error = new Error('ruh roh');
+ var error2 = new Error('ruh roh');
+
+ var fn = function () {\r
+
+ Hoek.assert(false, error);\r
+ };
+
+ try {\r
+ fn();\r
+ } catch (err) {\r
+ expect(error).to.equal(error); // should be the same reference
+ expect(error).to.not.equal(error2); // error with the same message should not match
+ }
+
+ done();\r
+ });\r
+});
+
+describe('Timer', function () {\r
+
+ it('returns time elapsed', function (done) {\r
+
+ var timer = new Hoek.Timer();
+ setTimeout(function () {\r
+
+ expect(timer.elapsed()).to.be.above(9);
+ done();\r
+ }, 12);\r
+ });\r
+});
+
+describe('Bench', function () {\r
+
+ it('returns time elapsed', function (done) {\r
+
+ var timer = new Hoek.Bench();
+ setTimeout(function () {\r
+
+ expect(timer.elapsed()).to.be.above(9);
+ done();\r
+ }, 12);\r
+ });\r
+});
+
+describe('escapeRegex()', function () {\r
+
+ it('escapes all special regular expression characters', function (done) {\r
+
+ var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,');
+ expect(a).to.equal('4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`"\\(>\\)\\[<\\]d\\{\\}s\\,');
+ done();\r
+ });\r
+});
+
+describe('Base64Url', function () {\r
+
+ var base64str = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w';
+ var str = unescape('%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20%21%22%23%24%25%26%27%28%29*+%2C-./0123456789%3A%3B%3C%3D%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF');
+
+ describe('base64urlEncode()', function () {\r
+
+ it('should base64 URL-safe a string', function (done) {\r
+
+ expect(Hoek.base64urlEncode(str)).to.equal(base64str);
+ done();\r
+ });
+
+ it('encodes a buffer', function (done) {\r
+
+ expect(Hoek.base64urlEncode(new Buffer(str, 'binary'))).to.equal(base64str);
+ done();\r
+ });
+
+ it('should base64 URL-safe a hex string', function (done) {\r
+
+ var buffer = new Buffer(str, 'binary');
+ expect(Hoek.base64urlEncode(buffer.toString('hex'), 'hex')).to.equal(base64str);
+ done();\r
+ });
+
+ it('works on larger input strings', function (done) {\r
+
+ var input = Fs.readFileSync(Path.join(__dirname, 'index.js')).toString();
+ var encoded = Hoek.base64urlEncode(input);
+
+ expect(encoded).to.not.contain('+');
+ expect(encoded).to.not.contain('/');
+
+ var decoded = Hoek.base64urlDecode(encoded);
+
+ expect(decoded).to.equal(input);
+ done();\r
+ });\r
+ });
+
+ describe('base64urlDecode()', function () {\r
+
+ it('should un-base64 URL-safe a string', function (done) {\r
+
+ expect(Hoek.base64urlDecode(base64str)).to.equal(str);
+ done();\r
+ });
+
+ it('should un-base64 URL-safe a string into hex', function (done) {\r
+
+ expect(Hoek.base64urlDecode(base64str, 'hex')).to.equal(new Buffer(str, 'binary').toString('hex'));
+ done();\r
+ });
+
+ it('should un-base64 URL-safe a string and return a buffer', function (done) {\r
+
+ var buf = Hoek.base64urlDecode(base64str, 'buffer');
+ expect(buf instanceof Buffer).to.equal(true);
+ expect(buf.toString('binary')).to.equal(str);
+ done();\r
+ });
+
+ it('returns error on undefined input', function (done) {\r
+
+ expect(Hoek.base64urlDecode().message).to.exist();
+ done();\r
+ });
+
+ it('returns error on invalid input', function (done) {\r
+
+ expect(Hoek.base64urlDecode('*').message).to.exist();
+ done();\r
+ });\r
+ });\r
+});
+
+describe('escapeHeaderAttribute()', function () {\r
+
+ it('should not alter ascii values', function (done) {\r
+
+ var a = Hoek.escapeHeaderAttribute('My Value');
+ expect(a).to.equal('My Value');
+ done();\r
+ });
+
+ it('escapes all special HTTP header attribute characters', function (done) {\r
+
+ var a = Hoek.escapeHeaderAttribute('I said go!!!#"' + String.fromCharCode(92));
+ expect(a).to.equal('I said go!!!#\\"\\\\');
+ done();\r
+ });
+
+ it('throws on large unicode characters', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.escapeHeaderAttribute('this is a test' + String.fromCharCode(500) + String.fromCharCode(300));\r
+ };
+
+ expect(fn).to.throw(Error);
+ done();\r
+ });
+
+ it('throws on CRLF to prevent response splitting', function (done) {\r
+
+ var fn = function () {\r
+
+ Hoek.escapeHeaderAttribute('this is a test\r\n');\r
+ };
+
+ expect(fn).to.throw(Error);
+ done();\r
+ });\r
+});
+
+describe('escapeHtml()', function () {\r
+
+ it('escapes all special HTML characters', function (done) {\r
+
+ var a = Hoek.escapeHtml('&<>"\'`');
+ expect(a).to.equal('&<>"'`');
+ done();\r
+ });
+
+ it('returns empty string on falsy input', function (done) {\r
+
+ var a = Hoek.escapeHtml('');
+ expect(a).to.equal('');
+ done();\r
+ });
+
+ it('returns unchanged string on no reserved input', function (done) {\r
+
+ var a = Hoek.escapeHtml('abc');
+ expect(a).to.equal('abc');
+ done();\r
+ });\r
+});
+
+describe('nextTick()', function () {\r
+
+ it('calls the provided callback on nextTick', function (done) {\r
+
+ var a = 0;
+
+ var inc = function (step, next) {\r
+
+ a += step;
+ next();\r
+ };
+
+ var ticked = Hoek.nextTick(inc);
+
+ ticked(5, function () {\r
+
+ expect(a).to.equal(6);
+ done();\r
+ });
+
+ expect(a).to.equal(0);
+ inc(1, function () {\r
+
+ expect(a).to.equal(1);\r
+ });\r
+ });\r
+});
+
+describe('once()', function () {\r
+
+ it('allows function to only execute once', function (done) {\r
+
+ var gen = 0;
+ var add = function (x) {\r
+
+ gen += x;\r
+ };
+
+ add(5);
+ expect(gen).to.equal(5);
+ add = Hoek.once(add);
+ add(5);
+ expect(gen).to.equal(10);
+ add(5);
+ expect(gen).to.equal(10);
+ done();\r
+ });
+
+ it('double once wraps one time', function (done) {\r
+
+ var method = function () { };
+ method = Hoek.once(method);
+ method.x = 1;
+ method = Hoek.once(method);
+ expect(method.x).to.equal(1);
+ done();\r
+ });\r
+});
+
+describe('isAbsoltePath()', function () {\r
+
+ it('identifies if path is absolute on Unix without node support', { parallel: false }, function (done) {\r
+
+ var orig = Path.isAbsolute;
+ Path.isAbsolute = undefined;
+
+ expect(Hoek.isAbsolutePath('')).to.equal(false);
+ expect(Hoek.isAbsolutePath('a')).to.equal(false);
+ expect(Hoek.isAbsolutePath('./a')).to.equal(false);
+ expect(Hoek.isAbsolutePath('/a')).to.equal(true);
+ expect(Hoek.isAbsolutePath('/')).to.equal(true);
+
+ Path.isAbsolute = orig;
+
+ done();\r
+ });
+
+ it('identifies if path is absolute with fake node support', { parallel: false }, function (done) {\r
+
+ var orig = Path.isAbsolute;
+ Path.isAbsolute = function (path) {\r
+\r
+ return path[0] === '/';\r
+ };
+
+ expect(Hoek.isAbsolutePath('', 'linux')).to.equal(false);
+ expect(Hoek.isAbsolutePath('a', 'linux')).to.equal(false);
+ expect(Hoek.isAbsolutePath('./a', 'linux')).to.equal(false);
+ expect(Hoek.isAbsolutePath('/a', 'linux')).to.equal(true);
+ expect(Hoek.isAbsolutePath('/', 'linux')).to.equal(true);
+
+ Path.isAbsolute = orig;
+
+ done();\r
+ });
+
+ it('identifies if path is absolute on Windows without node support', { parallel: false }, function (done) {\r
+
+ var orig = Path.isAbsolute;
+ Path.isAbsolute = undefined;
+
+ expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true);
+ expect(Hoek.isAbsolutePath('//server/file', 'win32')).to.equal(true);
+ expect(Hoek.isAbsolutePath('\\\\server\\file', 'win32')).to.equal(true);
+ expect(Hoek.isAbsolutePath('C:/Users/', 'win32')).to.equal(true);
+ expect(Hoek.isAbsolutePath('C:\\Users\\', 'win32')).to.equal(true);
+ expect(Hoek.isAbsolutePath('C:cwd/another', 'win32')).to.equal(false);
+ expect(Hoek.isAbsolutePath('C:cwd\\another', 'win32')).to.equal(false);
+ expect(Hoek.isAbsolutePath('directory/directory', 'win32')).to.equal(false);
+ expect(Hoek.isAbsolutePath('directory\\directory', 'win32')).to.equal(false);
+
+ Path.isAbsolute = orig;
+
+ done();\r
+ });\r
+});
+
+describe('isInteger()', function () {\r
+
+ it('validates integers', function (done) {\r
+
+ expect(Hoek.isInteger(0)).to.equal(true);
+ expect(Hoek.isInteger(1)).to.equal(true);
+ expect(Hoek.isInteger(1394035612500)).to.equal(true);
+ expect(Hoek.isInteger('0')).to.equal(false);
+ expect(Hoek.isInteger(1.0)).to.equal(true);
+ expect(Hoek.isInteger(1.1)).to.equal(false);
+ done();\r
+ });\r
+});
+
+describe('ignore()', function () {\r
+
+ it('exists', function (done) {\r
+
+ expect(Hoek.ignore).to.exist();
+ expect(typeof Hoek.ignore).to.equal('function');
+ done();\r
+ });\r
+});
+
+describe('inherits()', function () {\r
+
+ it('exists', function (done) {\r
+
+ expect(Hoek.inherits).to.exist();
+ expect(typeof Hoek.inherits).to.equal('function');
+ done();\r
+ });\r
+});
+
+describe('format()', function () {\r
+
+ it('exists', function (done) {\r
+
+ expect(Hoek.format).to.exist();
+ expect(typeof Hoek.format).to.equal('function');
+ done();\r
+ });
+
+ it('is a reference to Util.format', function (done) {\r
+
+ expect(Hoek.format('hello %s', 'world')).to.equal('hello world');
+ done();\r
+ });\r
+});
+
+describe('transform()', function () {\r
+
+ var source = {\r
+ address: {\r
+ one: '123 main street',
+ two: 'PO Box 1234'\r
+ },
+ zip: {\r
+ code: 3321232,
+ province: null\r
+ },
+ title: 'Warehouse',
+ state: 'CA'\r
+ };
+
+ it('transforms an object based on the input object', function (done) {\r
+
+ var result = Hoek.transform(source, {\r
+ 'person.address.lineOne': 'address.one',
+ 'person.address.lineTwo': 'address.two',
+ 'title': 'title',
+ 'person.address.region': 'state',
+ 'person.address.zip': 'zip.code',
+ 'person.address.location': 'zip.province'\r
+ });
+
+ expect(result).to.deep.equal({\r
+ person: {\r
+ address: {\r
+ lineOne: '123 main street',
+ lineTwo: 'PO Box 1234',
+ region: 'CA',
+ zip: 3321232,
+ location: null\r
+ }\r
+ },
+ title: 'Warehouse'\r
+ });
+
+ done();\r
+ });
+
+ it('uses the reach options passed into it', function (done) {\r
+
+ var schema = {\r
+ 'person.address.lineOne': 'address-one',
+ 'person.address.lineTwo': 'address-two',
+ 'title': 'title',
+ 'person.address.region': 'state',
+ 'person.prefix': 'person-title',
+ 'person.zip': 'zip-code'\r
+ };
+ var options = {\r
+ separator: '-',
+ default: 'unknown'\r
+ };
+ var result = Hoek.transform(source, schema, options);
+
+ expect(result).to.deep.equal({\r
+ person: {\r
+ address: {\r
+ lineOne: '123 main street',
+ lineTwo: 'PO Box 1234',
+ region: 'CA'\r
+ },
+ prefix: 'unknown',
+ zip: 3321232\r
+ },
+ title: 'Warehouse'\r
+ });
+
+ done();\r
+ });
+
+ it('works to create shallow objects', function (done) {\r
+
+ var result = Hoek.transform(source, {\r
+ lineOne: 'address.one',
+ lineTwo: 'address.two',
+ title: 'title',
+ region: 'state',
+ province: 'zip.province'\r
+ });
+
+ expect(result).to.deep.equal({\r
+ lineOne: '123 main street',
+ lineTwo: 'PO Box 1234',
+ title: 'Warehouse',
+ region: 'CA',
+ province: null\r
+ });
+
+ done();\r
+ });
+
+ it('only allows strings in the map', function (done) {\r
+
+ expect(function () {
+
+ var result = Hoek.transform(source, {
+ lineOne: {}
+ });
+ }).to.throw('All mappings must be "." delineated strings');
+
+ done();\r
+ });
+
+ it('throws an error on invalid arguments', function (done) {\r
+
+ expect(function () {\r
+
+ var result = Hoek.transform(NaN, {});\r
+ }).to.throw('Invalid source object: must be null, undefined, or an object');
+
+ done();\r
+ });
+
+ it('is safe to pass null', function (done) {\r
+
+ var result = Hoek.transform(null, {});
+ expect(result).to.deep.equal({});
+
+ done();\r
+ });
+
+ it('is safe to pass undefined', function (done) {\r
+
+ var result = Hoek.transform(undefined, {});
+ expect(result).to.deep.equal({});
+
+ done();\r
+ });\r
+});
+
+describe('uniqueFilename()', function () {\r
+
+ it('generates a random file path', function (done) {\r
+
+ var result = Hoek.uniqueFilename('./test/modules');
+
+ expect(result).to.exist();
+ expect(result).to.be.a.string();
+ expect(result).to.contain('test/modules');
+ done();\r
+ });
+
+ it('is random enough to use in fast loops', function (done) {\r
+
+ var results = [];
+
+ for (var i = 0; i < 10; ++i) {\r
+ results[i] = Hoek.uniqueFilename('./test/modules');\r
+ }
+
+ var filter = results.filter(function (item, index, array) {\r
+
+ return array.indexOf(item) === index;\r
+ });
+
+ expect(filter.length).to.equal(10);
+ expect(results.length).to.equal(10);
+ done();\r
+
+ });
+
+ it('combines the random elements with a supplied character', function (done) {\r
+
+ var result = Hoek.uniqueFilename('./test', 'txt');
+
+ expect(result).to.contain('test/');
+ expect(result).to.contain('.txt');
+
+ done();\r
+ });
+
+ it('accepts extensions with a "." in it', function (done) {\r
+
+ var result = Hoek.uniqueFilename('./test', '.mp3');
+
+ expect(result).to.contain('test/');
+ expect(result).to.contain('.mp3');
+
+ done();\r
+ });\r
+});
+
+describe('stringify()', function (done) {\r
+
+ it('converts object to string', function (done) {\r
+
+ var obj = { a: 1 };
+ expect(Hoek.stringify(obj)).to.equal('{"a":1}');
+ done();\r
+ });
+
+ it('returns error in result string', function (done) {\r
+
+ var obj = { a: 1 };
+ obj.b = obj;
+ expect(Hoek.stringify(obj)).to.equal('[Cannot display object: Converting circular structure to JSON]');
+ done();\r
+ });\r
+});
+
+describe('shallow()', function (done) {\r
+
+ it('shallow copies an object', function (done) {\r
+
+ var obj = {\r
+ a: 5,
+ b: {\r
+ c: 6\r
+ }\r
+ };
+
+ var shallow = Hoek.shallow(obj);
+ expect(shallow).to.not.equal(obj);
+ expect(shallow).to.deep.equal(obj);
+ expect(shallow.b).to.equal(obj.b);
+ done();\r
+ });\r
+});
--- /dev/null
+exports.x = 1;
--- /dev/null
+exports.y = 2;
--- /dev/null
+exports.z = 3;
--- /dev/null
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+
--- /dev/null
+language: node_js
+
+node_js:
+ - 0.10
+
--- /dev/null
+Copyright (c) 2012-2014, Eran Hammer and other contributors.
+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.
+ * The names of any contributors may not 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS 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.
+
+ * * *
+
+The complete list of contributors can be found at: https://github.com/hueniverse/sntp/graphs/contributors
--- /dev/null
+test:
+ @node node_modules/lab/bin/lab
+test-cov:
+ @node node_modules/lab/bin/lab -t 100 -m 3000
+test-cov-html:
+ @node node_modules/lab/bin/lab -r html -o coverage.html
+
+.PHONY: test test-cov test-cov-html
+
--- /dev/null
+# sntp
+
+An SNTP v4 client (RFC4330) for node. Simpy connects to the NTP or SNTP server requested and returns the server time
+along with the roundtrip duration and clock offset. To adjust the local time to the NTP time, add the returned `t` offset
+to the local time.
+
+[](http://travis-ci.org/hueniverse/sntp)
+
+# Usage
+
+```javascript
+var Sntp = require('sntp');
+
+// All options are optional
+
+var options = {
+ host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org
+ port: 123, // Defaults to 123 (NTP)
+ resolveReference: true, // Default to false (not resolving)
+ timeout: 1000 // Defaults to zero (no timeout)
+};
+
+// Request server time
+
+Sntp.time(options, function (err, time) {
+
+ if (err) {
+ console.log('Failed: ' + err.message);
+ process.exit(1);
+ }
+
+ console.log('Local clock is off by: ' + time.t + ' milliseconds');
+ process.exit(0);
+});
+```
+
+If an application needs to maintain continuous time synchronization, the module provides a stateful method for
+querying the current offset only when the last one is too old (defaults to daily).
+
+```javascript
+// Request offset once
+
+Sntp.offset(function (err, offset) {
+
+ console.log(offset); // New (served fresh)
+
+ // Request offset again
+
+ Sntp.offset(function (err, offset) {
+
+ console.log(offset); // Identical (served from cache)
+ });
+});
+```
+
+To set a background offset refresh, start the interval and use the provided now() method. If for any reason the
+client fails to obtain an up-to-date offset, the current system clock is used.
+
+```javascript
+var before = Sntp.now(); // System time without offset
+
+Sntp.start(function () {
+
+ var now = Sntp.now(); // With offset
+ Sntp.stop();
+});
+```
+
--- /dev/null
+var Sntp = require('../lib');
+
+// Request offset once
+
+Sntp.offset(function (err, offset) {
+
+ console.log(offset); // New (served fresh)
+
+ // Request offset again
+
+ Sntp.offset(function (err, offset) {
+
+ console.log(offset); // Identical (served from cache)
+ });
+});
+
--- /dev/null
+var Sntp = require('../lib');
+
+// All options are optional
+
+var options = {
+ host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org
+ port: 123, // Defaults to 123 (NTP)
+ resolveReference: true, // Default to false (not resolving)
+ timeout: 1000 // Defaults to zero (no timeout)
+};
+
+// Request server time
+
+Sntp.time(options, function (err, time) {
+
+ if (err) {
+ console.log('Failed: ' + err.message);
+ process.exit(1);
+ }
+
+ console.log(time);
+ console.log('Local clock is off by: ' + time.t + ' milliseconds');
+ process.exit(0);
+});
+
--- /dev/null
+module.exports = require('./lib');
\ No newline at end of file
--- /dev/null
+// Load modules
+
+var Dgram = require('dgram');
+var Dns = require('dns');
+var Hoek = require('hoek');
+
+
+// Declare internals
+
+var internals = {};
+
+
+exports.time = function (options, callback) {
+
+ if (arguments.length !== 2) {
+ callback = arguments[0];
+ options = {};
+ }
+
+ var settings = Hoek.clone(options);
+ settings.host = settings.host || 'pool.ntp.org';
+ settings.port = settings.port || 123;
+ settings.resolveReference = settings.resolveReference || false;
+
+ // Declare variables used by callback
+
+ var timeoutId = 0;
+ var sent = 0;
+
+ // Ensure callback is only called once
+
+ var finish = function (err, result) {
+
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ timeoutId = 0;
+ }
+
+ socket.removeAllListeners();
+ socket.once('error', internals.ignore);
+ socket.close();
+ return callback(err, result);
+ };
+
+ finish = Hoek.once(finish);
+
+ // Create UDP socket
+
+ var socket = Dgram.createSocket('udp4');
+
+ socket.once('error', function (err) {
+
+ return finish(err);
+ });
+
+ // Listen to incoming messages
+
+ socket.on('message', function (buffer, rinfo) {
+
+ var received = Date.now();
+
+ var message = new internals.NtpMessage(buffer);
+ if (!message.isValid) {
+ return finish(new Error('Invalid server response'), message);
+ }
+
+ if (message.originateTimestamp !== sent) {
+ return finish(new Error('Wrong originate timestamp'), message);
+ }
+
+ // Timestamp Name ID When Generated
+ // ------------------------------------------------------------
+ // Originate Timestamp T1 time request sent by client
+ // Receive Timestamp T2 time request received by server
+ // Transmit Timestamp T3 time reply sent by server
+ // Destination Timestamp T4 time reply received by client
+ //
+ // The roundtrip delay d and system clock offset t are defined as:
+ //
+ // d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2
+
+ var T1 = message.originateTimestamp;
+ var T2 = message.receiveTimestamp;
+ var T3 = message.transmitTimestamp;
+ var T4 = received;
+
+ message.d = (T4 - T1) - (T3 - T2);
+ message.t = ((T2 - T1) + (T3 - T4)) / 2;
+ message.receivedLocally = received;
+
+ if (!settings.resolveReference ||
+ message.stratum !== 'secondary') {
+
+ return finish(null, message);
+ }
+
+ // Resolve reference IP address
+
+ Dns.reverse(message.referenceId, function (err, domains) {
+
+ if (/* $lab:coverage:off$ */ !err /* $lab:coverage:on$ */) {
+ message.referenceHost = domains[0];
+ }
+
+ return finish(null, message);
+ });
+ });
+
+ // Set timeout
+
+ if (settings.timeout) {
+ timeoutId = setTimeout(function () {
+
+ timeoutId = 0;
+ return finish(new Error('Timeout'));
+ }, settings.timeout);
+ }
+
+ // Construct NTP message
+
+ var message = new Buffer(48);
+ for (var i = 0; i < 48; i++) { // Zero message
+ message[i] = 0;
+ }
+
+ message[0] = (0 << 6) + (4 << 3) + (3 << 0) // Set version number to 4 and Mode to 3 (client)
+ sent = Date.now();
+ internals.fromMsecs(sent, message, 40); // Set transmit timestamp (returns as originate)
+
+ // Send NTP request
+
+ socket.send(message, 0, message.length, settings.port, settings.host, function (err, bytes) {
+
+ if (err ||
+ bytes !== 48) {
+
+ return finish(err || new Error('Could not send entire message'));
+ }
+ });
+};
+
+
+internals.NtpMessage = function (buffer) {
+
+ this.isValid = false;
+
+ // Validate
+
+ if (buffer.length !== 48) {
+ return;
+ }
+
+ // Leap indicator
+
+ var li = (buffer[0] >> 6);
+ switch (li) {
+ case 0: this.leapIndicator = 'no-warning'; break;
+ case 1: this.leapIndicator = 'last-minute-61'; break;
+ case 2: this.leapIndicator = 'last-minute-59'; break;
+ case 3: this.leapIndicator = 'alarm'; break;
+ }
+
+ // Version
+
+ var vn = ((buffer[0] & 0x38) >> 3);
+ this.version = vn;
+
+ // Mode
+
+ var mode = (buffer[0] & 0x7);
+ switch (mode) {
+ case 1: this.mode = 'symmetric-active'; break;
+ case 2: this.mode = 'symmetric-passive'; break;
+ case 3: this.mode = 'client'; break;
+ case 4: this.mode = 'server'; break;
+ case 5: this.mode = 'broadcast'; break;
+ case 0:
+ case 6:
+ case 7: this.mode = 'reserved'; break;
+ }
+
+ // Stratum
+
+ var stratum = buffer[1];
+ if (stratum === 0) {
+ this.stratum = 'death';
+ }
+ else if (stratum === 1) {
+ this.stratum = 'primary';
+ }
+ else if (stratum <= 15) {
+ this.stratum = 'secondary';
+ }
+ else {
+ this.stratum = 'reserved';
+ }
+
+ // Poll interval (msec)
+
+ this.pollInterval = Math.round(Math.pow(2, buffer[2])) * 1000;
+
+ // Precision (msecs)
+
+ this.precision = Math.pow(2, buffer[3]) * 1000;
+
+ // Root delay (msecs)
+
+ var rootDelay = 256 * (256 * (256 * buffer[4] + buffer[5]) + buffer[6]) + buffer[7];
+ this.rootDelay = 1000 * (rootDelay / 0x10000);
+
+ // Root dispersion (msecs)
+
+ this.rootDispersion = ((buffer[8] << 8) + buffer[9] + ((buffer[10] << 8) + buffer[11]) / Math.pow(2, 16)) * 1000;
+
+ // Reference identifier
+
+ this.referenceId = '';
+ switch (this.stratum) {
+ case 'death':
+ case 'primary':
+ this.referenceId = String.fromCharCode(buffer[12]) + String.fromCharCode(buffer[13]) + String.fromCharCode(buffer[14]) + String.fromCharCode(buffer[15]);
+ break;
+ case 'secondary':
+ this.referenceId = '' + buffer[12] + '.' + buffer[13] + '.' + buffer[14] + '.' + buffer[15];
+ break;
+ }
+
+ // Reference timestamp
+
+ this.referenceTimestamp = internals.toMsecs(buffer, 16);
+
+ // Originate timestamp
+
+ this.originateTimestamp = internals.toMsecs(buffer, 24);
+
+ // Receive timestamp
+
+ this.receiveTimestamp = internals.toMsecs(buffer, 32);
+
+ // Transmit timestamp
+
+ this.transmitTimestamp = internals.toMsecs(buffer, 40);
+
+ // Validate
+
+ if (this.version === 4 &&
+ this.stratum !== 'reserved' &&
+ this.mode === 'server' &&
+ this.originateTimestamp &&
+ this.receiveTimestamp &&
+ this.transmitTimestamp) {
+
+ this.isValid = true;
+ }
+
+ return this;
+};
+
+
+internals.toMsecs = function (buffer, offset) {
+
+ var seconds = 0;
+ var fraction = 0;
+
+ for (var i = 0; i < 4; ++i) {
+ seconds = (seconds * 256) + buffer[offset + i];
+ }
+
+ for (i = 4; i < 8; ++i) {
+ fraction = (fraction * 256) + buffer[offset + i];
+ }
+
+ return ((seconds - 2208988800 + (fraction / Math.pow(2, 32))) * 1000);
+};
+
+
+internals.fromMsecs = function (ts, buffer, offset) {
+
+ var seconds = Math.floor(ts / 1000) + 2208988800;
+ var fraction = Math.round((ts % 1000) / 1000 * Math.pow(2, 32));
+
+ buffer[offset + 0] = (seconds & 0xFF000000) >> 24;
+ buffer[offset + 1] = (seconds & 0x00FF0000) >> 16;
+ buffer[offset + 2] = (seconds & 0x0000FF00) >> 8;
+ buffer[offset + 3] = (seconds & 0x000000FF);
+
+ buffer[offset + 4] = (fraction & 0xFF000000) >> 24;
+ buffer[offset + 5] = (fraction & 0x00FF0000) >> 16;
+ buffer[offset + 6] = (fraction & 0x0000FF00) >> 8;
+ buffer[offset + 7] = (fraction & 0x000000FF);
+};
+
+
+// Offset singleton
+
+internals.last = {
+ offset: 0,
+ expires: 0,
+ host: '',
+ port: 0
+};
+
+
+exports.offset = function (options, callback) {
+
+ if (arguments.length !== 2) {
+ callback = arguments[0];
+ options = {};
+ }
+
+ var now = Date.now();
+ var clockSyncRefresh = options.clockSyncRefresh || 24 * 60 * 60 * 1000; // Daily
+
+ if (internals.last.offset &&
+ internals.last.host === options.host &&
+ internals.last.port === options.port &&
+ now < internals.last.expires) {
+
+ process.nextTick(function () {
+
+ callback(null, internals.last.offset);
+ });
+
+ return;
+ }
+
+ exports.time(options, function (err, time) {
+
+ if (err) {
+ return callback(err, 0);
+ }
+
+ internals.last = {
+ offset: Math.round(time.t),
+ expires: now + clockSyncRefresh,
+ host: options.host,
+ port: options.port
+ };
+
+ return callback(null, internals.last.offset);
+ });
+};
+
+
+// Now singleton
+
+internals.now = {
+ intervalId: 0
+};
+
+
+exports.start = function (options, callback) {
+
+ if (arguments.length !== 2) {
+ callback = arguments[0];
+ options = {};
+ }
+
+ if (internals.now.intervalId) {
+ process.nextTick(function () {
+
+ callback();
+ });
+
+ return;
+ }
+
+ exports.offset(options, function (err, offset) {
+
+ internals.now.intervalId = setInterval(function () {
+
+ exports.offset(options, function () { });
+ }, options.clockSyncRefresh || 24 * 60 * 60 * 1000); // Daily
+
+ return callback();
+ });
+};
+
+
+exports.stop = function () {
+
+ if (!internals.now.intervalId) {
+ return;
+ }
+
+ clearInterval(internals.now.intervalId);
+ internals.now.intervalId = 0;
+};
+
+
+exports.isLive = function () {
+
+ return !!internals.now.intervalId;
+};
+
+
+exports.now = function () {
+
+ var now = Date.now();
+ if (!exports.isLive() ||
+ now >= internals.last.expires) {
+
+ return now;
+ }
+
+ return now + internals.last.offset;
+};
+
+
+internals.ignore = function () {
+
+};
--- /dev/null
+{
+ "name": "sntp",
+ "description": "SNTP Client",
+ "version": "1.0.9",
+ "author": {
+ "name": "Eran Hammer",
+ "email": "eran@hammer.io",
+ "url": "http://hueniverse.com"
+ },
+ "contributors": [],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/hueniverse/sntp.git"
+ },
+ "main": "index",
+ "keywords": [
+ "sntp",
+ "ntp",
+ "time"
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "dependencies": {
+ "hoek": "2.x.x"
+ },
+ "devDependencies": {
+ "lab": "4.x.x"
+ },
+ "scripts": {
+ "test": "make test-cov"
+ },
+ "licenses": [
+ {
+ "type": "BSD",
+ "url": "http://github.com/hueniverse/sntp/raw/master/LICENSE"
+ }
+ ],
+ "gitHead": "ee2e35284f684609990681734d39010cd356d7da",
+ "bugs": {
+ "url": "https://github.com/hueniverse/sntp/issues"
+ },
+ "homepage": "https://github.com/hueniverse/sntp",
+ "_id": "sntp@1.0.9",
+ "_shasum": "6541184cc90aeea6c6e7b35e2659082443c66198",
+ "_from": "sntp@>=1.0.0 <2.0.0",
+ "_npmVersion": "1.4.23",
+ "_npmUser": {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ },
+ "maintainers": [
+ {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ }
+ ],
+ "dist": {
+ "shasum": "6541184cc90aeea6c6e7b35e2659082443c66198",
+ "tarball": "http://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+// Load modules
+
+var Dns = require('dns');
+var Dgram = require('dgram');
+var Lab = require('lab');
+var Sntp = require('../lib');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var before = lab.before;
+var after = lab.after;
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Lab.expect;
+
+
+describe('SNTP', function () {
+
+ describe('#time', function () {
+
+ it('returns consistent result over multiple tries', function (done) {
+
+ Sntp.time(function (err, time) {
+
+ expect(err).to.not.exist;
+ expect(time).to.exist;
+ var t1 = time.t;
+
+ Sntp.time(function (err, time) {
+
+ expect(err).to.not.exist;
+ expect(time).to.exist;
+ var t2 = time.t;
+ expect(Math.abs(t1 - t2)).is.below(200);
+ done();
+ });
+ });
+ });
+
+ it('resolves reference IP', function (done) {
+
+ Sntp.time({ host: 'ntp.exnet.com', resolveReference: true }, function (err, time) {
+
+ expect(err).to.not.exist;
+ expect(time).to.exist;
+ expect(time.referenceHost).to.exist;
+ done();
+ });
+ });
+
+ it('times out on no response', function (done) {
+
+ Sntp.time({ port: 124, timeout: 100 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time).to.not.exist;
+ expect(err.message).to.equal('Timeout');
+ done();
+ });
+ });
+
+ it('errors on error event', { parallel: false }, function (done) {
+
+ var orig = Dgram.createSocket;
+ Dgram.createSocket = function (type) {
+
+ Dgram.createSocket = orig;
+ var socket = Dgram.createSocket(type);
+ setImmediate(function () { socket.emit('error', new Error('Fake')) });
+ return socket;
+ };
+
+ Sntp.time(function (err, time) {
+
+ expect(err).to.exist;
+ expect(time).to.not.exist;
+ expect(err.message).to.equal('Fake');
+ done();
+ });
+ });
+
+ it('errors on incorrect sent size', { parallel: false }, function (done) {
+
+ var orig = Dgram.Socket.prototype.send;
+ Dgram.Socket.prototype.send = function (buf, offset, length, port, address, callback) {
+
+ Dgram.Socket.prototype.send = orig;
+ return callback(null, 40);
+ };
+
+ Sntp.time(function (err, time) {
+
+ expect(err).to.exist;
+ expect(time).to.not.exist;
+ expect(err.message).to.equal('Could not send entire message');
+ done();
+ });
+ });
+
+ it('times out on invalid host', function (done) {
+
+ Sntp.time({ host: 'error', timeout: 10000 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time).to.not.exist;
+ expect(err.message).to.contain('getaddrinfo');
+ done();
+ });
+ });
+
+ it('fails on bad response buffer size', function (done) {
+
+ var server = Dgram.createSocket('udp4');
+ server.on('message', function (message, remote) {
+ var message = new Buffer(10);
+ server.send(message, 0, message.length, remote.port, remote.address, function (err, bytes) {
+
+ server.close();
+ });
+ });
+
+ server.bind(49123);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(err.message).to.equal('Invalid server response');
+ done();
+ });
+ });
+
+ var messup = function (bytes) {
+
+ var server = Dgram.createSocket('udp4');
+ server.on('message', function (message, remote) {
+
+ var message = new Buffer([
+ 0x24, 0x01, 0x00, 0xe3,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x43, 0x54, 0x53,
+ 0xd4, 0xa8, 0x2d, 0xc7,
+ 0x1c, 0x5d, 0x49, 0x1b,
+ 0xd4, 0xa8, 0x2d, 0xe6,
+ 0x67, 0xef, 0x9d, 0xb2,
+ 0xd4, 0xa8, 0x2d, 0xe6,
+ 0x71, 0xed, 0xb5, 0xfb,
+ 0xd4, 0xa8, 0x2d, 0xe6,
+ 0x71, 0xee, 0x6c, 0xc5
+ ]);
+
+ for (var i = 0, il = bytes.length; i < il; ++i) {
+ message[bytes[i][0]] = bytes[i][1];
+ }
+
+ server.send(message, 0, message.length, remote.port, remote.address, function (err, bytes) {
+
+ server.close();
+ });
+ });
+
+ server.bind(49123);
+ };
+
+ it('fails on bad version', function (done) {
+
+ messup([[0, (0 << 6) + (3 << 3) + (4 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time.version).to.equal(3);
+ expect(err.message).to.equal('Invalid server response');
+ done();
+ });
+ });
+
+ it('fails on bad originateTimestamp', function (done) {
+
+ messup([[24, 0x83], [25, 0xaa], [26, 0x7e], [27, 0x80], [28, 0], [29, 0], [30, 0], [31, 0]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(err.message).to.equal('Invalid server response');
+ done();
+ });
+ });
+
+ it('fails on bad receiveTimestamp', function (done) {
+
+ messup([[32, 0x83], [33, 0xaa], [34, 0x7e], [35, 0x80], [36, 0], [37, 0], [38, 0], [39, 0]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(err.message).to.equal('Invalid server response');
+ done();
+ });
+ });
+
+ it('fails on bad originate timestamp and alarm li', function (done) {
+
+ messup([[0, (3 << 6) + (4 << 3) + (4 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(err.message).to.equal('Wrong originate timestamp');
+ expect(time.leapIndicator).to.equal('alarm');
+ done();
+ });
+ });
+
+ it('returns time with death stratum and last61 li', function (done) {
+
+ messup([[0, (1 << 6) + (4 << 3) + (4 << 0)], [1, 0]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(time.stratum).to.equal('death');
+ expect(time.leapIndicator).to.equal('last-minute-61');
+ done();
+ });
+ });
+
+ it('returns time with reserved stratum and last59 li', function (done) {
+
+ messup([[0, (2 << 6) + (4 << 3) + (4 << 0)], [1, 0x1f]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(time.stratum).to.equal('reserved');
+ expect(time.leapIndicator).to.equal('last-minute-59');
+ done();
+ });
+ });
+
+ it('fails on bad mode (symmetric-active)', function (done) {
+
+ messup([[0, (0 << 6) + (4 << 3) + (1 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time.mode).to.equal('symmetric-active');
+ done();
+ });
+ });
+
+ it('fails on bad mode (symmetric-passive)', function (done) {
+
+ messup([[0, (0 << 6) + (4 << 3) + (2 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time.mode).to.equal('symmetric-passive');
+ done();
+ });
+ });
+
+ it('fails on bad mode (client)', function (done) {
+
+ messup([[0, (0 << 6) + (4 << 3) + (3 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time.mode).to.equal('client');
+ done();
+ });
+ });
+
+ it('fails on bad mode (broadcast)', function (done) {
+
+ messup([[0, (0 << 6) + (4 << 3) + (5 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time.mode).to.equal('broadcast');
+ done();
+ });
+ });
+
+ it('fails on bad mode (reserved)', function (done) {
+
+ messup([[0, (0 << 6) + (4 << 3) + (6 << 0)]]);
+
+ Sntp.time({ host: 'localhost', port: 49123 }, function (err, time) {
+
+ expect(err).to.exist;
+ expect(time.mode).to.equal('reserved');
+ done();
+ });
+ });
+ });
+
+ describe('#offset', function () {
+
+ it('gets the current offset', function (done) {
+
+ Sntp.offset(function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.not.equal(0);
+ done();
+ });
+ });
+
+ it('gets the current offset from cache', function (done) {
+
+ Sntp.offset(function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.not.equal(0);
+ var offset1 = offset;
+ Sntp.offset({}, function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.equal(offset1);
+ done();
+ });
+ });
+ });
+
+ it('gets the new offset on different server', function (done) {
+
+ Sntp.offset(function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.not.equal(0);
+ var offset1 = offset;
+ Sntp.offset({ host: 'nist1-sj.ustiming.org' }, function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.not.equal(offset1);
+ done();
+ });
+ });
+ });
+
+ it('gets the new offset on different server', function (done) {
+
+ Sntp.offset(function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.not.equal(0);
+ var offset1 = offset;
+ Sntp.offset({ port: 123 }, function (err, offset) {
+
+ expect(err).to.not.exist;
+ expect(offset).to.not.equal(offset1);
+ done();
+ });
+ });
+ });
+
+ it('fails getting the current offset on invalid server', function (done) {
+
+ Sntp.offset({ host: 'error' }, function (err, offset) {
+
+ expect(err).to.exist;
+ expect(offset).to.equal(0);
+ done();
+ });
+ });
+ });
+
+ describe('#now', function () {
+
+ it('starts auto-sync, gets now, then stops', function (done) {
+
+ Sntp.stop();
+
+ var before = Sntp.now();
+ expect(before).to.equal(Date.now());
+
+ Sntp.start(function () {
+
+ var now = Sntp.now();
+ expect(now).to.not.equal(Date.now());
+ Sntp.stop();
+
+ done();
+ });
+ });
+
+ it('starts twice', function (done) {
+
+ Sntp.start(function () {
+
+ Sntp.start(function () {
+
+ var now = Sntp.now();
+ expect(now).to.not.equal(Date.now());
+ Sntp.stop();
+
+ done();
+ });
+ });
+ });
+
+ it('starts auto-sync, gets now, waits, gets again after timeout', function (done) {
+
+ Sntp.stop();
+
+ var before = Sntp.now();
+ expect(before).to.equal(Date.now());
+
+ Sntp.start({ clockSyncRefresh: 100 }, function () {
+
+ var now = Sntp.now();
+ expect(now).to.not.equal(Date.now());
+ expect(now).to.equal(Sntp.now());
+
+ setTimeout(function () {
+
+ expect(Sntp.now()).to.not.equal(now);
+ Sntp.stop();
+ done();
+ }, 110);
+ });
+ });
+ });
+});
+
--- /dev/null
+{
+ "name": "hawk",
+ "description": "HTTP Hawk Authentication Scheme",
+ "version": "2.3.1",
+ "author": {
+ "name": "Eran Hammer",
+ "email": "eran@hammer.io",
+ "url": "http://hueniverse.com"
+ },
+ "contributors": [],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/hueniverse/hawk.git"
+ },
+ "main": "index",
+ "keywords": [
+ "http",
+ "authentication",
+ "scheme",
+ "hawk"
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "browser": "./lib/browser.js",
+ "dependencies": {
+ "hoek": "2.x.x",
+ "boom": "2.x.x",
+ "cryptiles": "2.x.x",
+ "sntp": "1.x.x"
+ },
+ "devDependencies": {
+ "code": "1.x.x",
+ "lab": "5.x.x"
+ },
+ "scripts": {
+ "test": "make test-cov"
+ },
+ "licenses": [
+ {
+ "type": "BSD",
+ "url": "http://github.com/hueniverse/hawk/raw/master/LICENSE"
+ }
+ ],
+ "gitHead": "492632da51ecedd5f59ce96f081860ad24ce6532",
+ "bugs": {
+ "url": "https://github.com/hueniverse/hawk/issues"
+ },
+ "homepage": "https://github.com/hueniverse/hawk",
+ "_id": "hawk@2.3.1",
+ "_shasum": "1e731ce39447fa1d0f6d707f7bceebec0fd1ec1f",
+ "_from": "hawk@>=2.3.0 <2.4.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ },
+ "maintainers": [
+ {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ }
+ ],
+ "dist": {
+ "shasum": "1e731ce39447fa1d0f6d707f7bceebec0fd1ec1f",
+ "tarball": "http://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+// Load modules
+
+var Url = require('url');
+var Code = require('code');
+var Hawk = require('../lib');
+var Hoek = require('hoek');
+var Lab = require('lab');
+var Browser = require('../lib/browser');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Browser', function () {
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ id: id,
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: (id === '1' ? 'sha1' : 'sha256'),
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ it('should generate a bewit then successfully authenticate it', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2',
+ host: 'example.com',
+ port: 80
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var bewit = Browser.client.bewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials, ttlSec: 60 * 60 * 24 * 365 * 100, ext: 'some-app-data' });
+ req.url += '&bewit=' + bewit;
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(attributes.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('should generate a bewit then successfully authenticate it (no ext)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2',
+ host: 'example.com',
+ port: 80
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var bewit = Browser.client.bewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials, ttlSec: 60 * 60 * 24 * 365 * 100 });
+ req.url += '&bewit=' + bewit;
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+ });
+
+ describe('#bewit', function () {
+
+ it('returns a valid bewit value', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
+ done();
+ });
+
+ it('returns a valid bewit value (explicit HTTP port)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('http://example.com:8080/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcaFpiSjNQMmNLRW80a3kwQzhqa1pBa1J5Q1p1ZWc0V1NOYnhWN3ZxM3hIVT1ceGFuZHlhbmR6');
+ done();
+ });
+
+ it('returns a valid bewit value (explicit HTTPS port)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com:8043/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcL2t4UjhwK0xSaTdvQTRnUXc3cWlxa3BiVHRKYkR4OEtRMC9HRUwvVytTUT1ceGFuZHlhbmR6');
+ done();
+ });
+
+ it('returns a valid bewit value (null ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: null });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcSUdZbUxnSXFMckNlOEN4dktQczRKbFdJQStValdKSm91d2dBUmlWaENBZz1c');
+ done();
+ });
+
+ it('errors on invalid options', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', 4);
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on missing uri', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid uri', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit(5, { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ var credentials = {
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid algorithm', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on missing options', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
+ };
+
+ var bewit = Browser.client.bewit('https://example.com/somewhere/over/the/rainbow');
+ expect(bewit).to.equal('');
+ done();
+ });
+ });
+
+ it('generates a header then successfully parse it (configuration)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (node request)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (browserify)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ },
+ getHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (time offset)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', localtimeOffsetMsec: 100000 }).field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 100000 }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (no server header options)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (no server header)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header with stale ts and successfully authenticate on second call', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ Browser.utils.setNtpOffset(60 * 60 * 1000);
+ var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' });
+ req.authorization = header.field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Stale timestamp');
+
+ var res = {
+ headers: {
+ 'www-authenticate': err.output.headers['WWW-Authenticate']
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);
+ expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true);
+ expect(Browser.utils.getNtpOffset()).to.equal(0);
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+ });
+
+ it('generates a header with stale ts and successfully authenticate on second call (manual localStorage)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var localStorage = new Browser.internals.LocalStorage();
+
+ Browser.utils.setStorage(localStorage)
+
+ Browser.utils.setNtpOffset(60 * 60 * 1000);
+ var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' });
+ req.authorization = header.field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Stale timestamp');
+
+ var res = {
+ headers: {
+ 'www-authenticate': err.output.headers['WWW-Authenticate']
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ expect(parseInt(localStorage.getItem('hawk_ntp_offset'))).to.equal(60 * 60 * 1000);
+ expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);
+ expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true);
+ expect(Browser.utils.getNtpOffset()).to.equal(0);
+ expect(parseInt(localStorage.getItem('hawk_ntp_offset'))).to.equal(0);
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+ });
+
+ it('generates a header then fails to parse it (missing server header hash)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(false);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (with hash)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it then validate payload', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload('hola!', credentials, artifacts)).to.be.true();
+ expect(Hawk.server.authenticatePayload('hello!', credentials, artifacts)).to.be.false();
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (app)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(artifacts.app).to.equal('asd23ased');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (app, dlg)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(artifacts.app).to.equal('asd23ased');
+ expect(artifacts.dlg).to.equal('23434szr3q4d');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then fail authentication due to bad hash', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad payload hash');
+ done();
+ });
+ });
+ });
+
+ it('generates a header for one resource then fail to authenticate another', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
+ req.url = '/something/else';
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(credentials).to.exist();
+ done();
+ });
+ });
+ });
+
+ describe('client', function () {
+
+ describe('#header', function () {
+
+ it('returns a valid authorization header (sha1)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var header = Browser.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');
+ done();
+ });
+
+ it('returns a valid authorization header (sha256)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
+ done();
+ });
+
+ it('returns a valid authorization header (empty payload)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var header = Browser.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: '' }).field;
+ expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"404ghL7K+hfyhByKKejFBRGgTjU=\", ext=\"Bazinga!\", mac=\"Bh1sj1DOfFRWOdi3ww52nLCJdBE=\"');
+ done();
+ });
+
+ it('returns a valid authorization header (no ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
+ done();
+ });
+
+ it('returns a valid authorization header (null ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
+ done();
+ });
+
+ it('returns a valid authorization header (uri object)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var uri = Browser.utils.parseUri('https://example.net/somewhere/over/the/rainbow');
+ var header = Browser.client.header(uri, 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
+ done();
+ });
+
+ it('errors on missing options', function (done) {
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST');
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on empty uri', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on invalid uri', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header(4, 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on missing method', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', '', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on invalid method', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 5, { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid credentials object');
+ done();
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ var credentials = {
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid credentials object');
+ done();
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ algorithm: 'sha256'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid credentials object');
+ done();
+ });
+
+ it('errors on invalid algorithm', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
+ };
+
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Unknown algorithm');
+ done();
+ });
+
+ it('uses a pre-calculated payload hash', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var options = { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' };
+ options.hash = Browser.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
+ done();
+ });
+ });
+
+ describe('#authenticate', function () {
+
+ it('skips tsm validation when missing ts', function (done) {
+
+ var res = {
+ headers: {
+ 'www-authenticate': 'Hawk error="Stale timestamp"'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ ts: 1402135580,
+ nonce: 'iBRB6t',
+ method: 'GET',
+ resource: '/resource/4?filter=a',
+ host: 'example.com',
+ port: '8080',
+ ext: 'some-app-data'
+ };
+
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+
+ it('returns false on invalid header', function (done) {
+
+ var res = {
+ headers: {
+ 'server-authorization': 'Hawk mac="abc", bad="xyz"'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ expect(Browser.client.authenticate(res, {})).to.equal(false);
+ done();
+ });
+
+ it('returns false on invalid mac', function (done) {
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain',
+ 'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1362336900',
+ nonce: 'eb5S_L',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ app: undefined,
+ dlg: undefined,
+ mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
+ id: '123456'
+ };
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(false);
+ done();
+ });
+
+ it('returns true on ignoring hash', function (done) {
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain',
+ 'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1362336900',
+ nonce: 'eb5S_L',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ app: undefined,
+ dlg: undefined,
+ mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
+ id: '123456'
+ };
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+
+ it('errors on invalid WWW-Authenticate header format', function (done) {
+
+ var res = {
+ headers: {
+ 'www-authenticate': 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ expect(Browser.client.authenticate(res, {})).to.equal(false);
+ done();
+ });
+
+ it('errors on invalid WWW-Authenticate header format', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var res = {
+ headers: {
+ 'www-authenticate': 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"'
+ },
+ getResponseHeader: function (header) {
+
+ return res.headers[header.toLowerCase()];
+ }
+ };
+
+ expect(Browser.client.authenticate(res, credentials)).to.equal(false);
+ done();
+ });
+ });
+
+ describe('#message', function () {
+
+ it('generates an authorization then successfully parse it', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+ });
+
+ it('generates an authorization using custom nonce/timestamp', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials, nonce: 'abc123', timestamp: 1398536270957 });
+ expect(auth).to.exist();
+ expect(auth.nonce).to.equal('abc123');
+ expect(auth.ts).to.equal(1398536270957);
+ done();
+ });
+ });
+
+ it('errors on missing host', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message(null, 8080, 'some message', { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on invalid host', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message(5, 8080, 'some message', { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on missing port', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 0, 'some message', { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on invalid port', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 'a', 'some message', { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on missing message', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 8080, undefined, { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on null message', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 8080, null, { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on invalid message', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Browser.client.message('example.com', 8080, 5, { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var auth = Browser.client.message('example.com', 8080, 'some message', {});
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on missing options', function (done) {
+
+ var auth = Browser.client.message('example.com', 8080, 'some message');
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var creds = Hoek.clone(credentials);
+ delete creds.id;
+ var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var creds = Hoek.clone(credentials);
+ delete creds.key;
+ var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on invalid algorithm', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var creds = Hoek.clone(credentials);
+ creds.algorithm = 'blah';
+ var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+ });
+
+ describe('#authenticateTimestamp', function (done) {
+
+ it('validates a timestamp', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var tsm = Hawk.crypto.timestampMessage(credentials);
+ expect(Browser.client.authenticateTimestamp(tsm, credentials)).to.equal(true);
+ done();
+ });
+ });
+
+ it('validates a timestamp without updating local time', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var offset = Browser.utils.getNtpOffset();
+ var tsm = Hawk.crypto.timestampMessage(credentials, 10000);
+ expect(Browser.client.authenticateTimestamp(tsm, credentials, false)).to.equal(true);
+ expect(offset).to.equal(Browser.utils.getNtpOffset());
+ done();
+ });
+ });
+
+ it('detects a bad timestamp', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var tsm = Hawk.crypto.timestampMessage(credentials);
+ tsm.ts = 4;
+ expect(Browser.client.authenticateTimestamp(tsm, credentials)).to.equal(false);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('internals', function () {
+
+ describe('LocalStorage', function () {
+
+ it('goes through the full lifecycle', function (done) {
+
+ var storage = new Browser.internals.LocalStorage();
+ expect(storage.length).to.equal(0);
+ expect(storage.getItem('a')).to.equal(null);
+ storage.setItem('a', 5);
+ expect(storage.length).to.equal(1);
+ expect(storage.key()).to.equal('a');
+ expect(storage.key(0)).to.equal('a');
+ expect(storage.getItem('a')).to.equal('5');
+ storage.setItem('b', 'test');
+ expect(storage.key()).to.equal('a');
+ expect(storage.key(0)).to.equal('a');
+ expect(storage.key(1)).to.equal('b');
+ expect(storage.length).to.equal(2);
+ expect(storage.getItem('b')).to.equal('test');
+ storage.removeItem('a');
+ expect(storage.length).to.equal(1);
+ expect(storage.getItem('a')).to.equal(null);
+ expect(storage.getItem('b')).to.equal('test');
+ storage.clear();
+ expect(storage.length).to.equal(0);
+ expect(storage.getItem('a')).to.equal(null);
+ expect(storage.getItem('b')).to.equal(null);
+ done();
+ });
+ });
+ });
+
+ describe('utils', function () {
+
+ describe('#setStorage', function () {
+
+ it('sets storage for the first time', function (done) {
+
+ Browser.utils.storage = new Browser.internals.LocalStorage(); // Reset state
+
+ expect(Browser.utils.storage.getItem('hawk_ntp_offset')).to.not.exist();
+ Browser.utils.storage.setItem('test', '1');
+ Browser.utils.setStorage(new Browser.internals.LocalStorage());
+ expect(Browser.utils.storage.getItem('test')).to.not.exist();
+ Browser.utils.storage.setItem('test', '2');
+ expect(Browser.utils.storage.getItem('test')).to.equal('2');
+ done();
+ });
+ });
+
+ describe('#setNtpOffset', function (done) {
+
+ it('catches localStorage errors', function (done) {
+
+ var orig = Browser.utils.storage.setItem;
+ var error = console.error;
+ var count = 0;
+ console.error = function () { if (count++ === 2) { console.error = error; } };
+ Browser.utils.storage.setItem = function () {
+
+ Browser.utils.storage.setItem = orig;
+ throw new Error()
+ };
+
+ expect(function () {
+ Browser.utils.setNtpOffset(100);
+ }).not.to.throw();
+
+ done();
+ });
+ });
+
+ describe('#parseAuthorizationHeader', function (done) {
+
+ it('returns null on missing header', function (done) {
+
+ expect(Browser.utils.parseAuthorizationHeader()).to.equal(null);
+ done();
+ });
+
+ it('returns null on bad header syntax (structure)', function (done) {
+
+ expect(Browser.utils.parseAuthorizationHeader('Hawk')).to.equal(null);
+ done();
+ });
+
+ it('returns null on bad header syntax (parts)', function (done) {
+
+ expect(Browser.utils.parseAuthorizationHeader(' ')).to.equal(null);
+ done();
+ });
+
+ it('returns null on bad scheme name', function (done) {
+
+ expect(Browser.utils.parseAuthorizationHeader('Basic asdasd')).to.equal(null);
+ done();
+ });
+
+ it('returns null on bad attribute value', function (done) {
+
+ expect(Browser.utils.parseAuthorizationHeader('Hawk test="\t"', ['test'])).to.equal(null);
+ done();
+ });
+
+ it('returns null on duplicated attribute', function (done) {
+
+ expect(Browser.utils.parseAuthorizationHeader('Hawk test="a", test="b"', ['test'])).to.equal(null);
+ done();
+ });
+ });
+
+ describe('#parseUri', function () {
+
+ it('returns empty port when unknown scheme', function (done) {
+
+ var uri = Browser.utils.parseUri('ftp://domain');
+ expect(uri.port).to.equal('');
+ done();
+ });
+
+ it('returns default port when missing', function (done) {
+
+ var uri = Browser.utils.parseUri('http://');
+ expect(uri.port).to.equal('80');
+ done();
+ });
+ });
+
+ var str = "https://www.google.ca/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=url";
+ var base64str = "aHR0cHM6Ly93d3cuZ29vZ2xlLmNhL3dlYmhwP3NvdXJjZWlkPWNocm9tZS1pbnN0YW50Jmlvbj0xJmVzcHY9MiZpZT1VVEYtOCNxPXVybA";
+
+ describe('#base64urlEncode', function () {
+
+ it('should base64 URL-safe decode a string', function (done) {
+
+ expect(Browser.utils.base64urlEncode(str)).to.equal(base64str);
+ done();
+ });
+
+ });
+
+ });
+});
--- /dev/null
+// Load modules
+
+var Url = require('url');
+var Code = require('code');
+var Hawk = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ describe('client', function () {
+
+ describe('#header', function () {
+
+ it('returns a valid authorization header (sha1)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var header = Hawk.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');
+ done();
+ });
+
+ it('returns a valid authorization header (sha256)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
+ done();
+ });
+
+ it('returns a valid authorization header (no ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
+ done();
+ });
+
+ it('returns a valid authorization header (null ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
+ done();
+ });
+
+ it('returns a valid authorization header (empty payload)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: '', contentType: 'text/plain' }).field;
+ expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", mac=\"U5k16YEzn3UnBHKeBzsDXn067Gu3R4YaY6xOt9PYRZM=\"');
+ done();
+ });
+
+ it('returns a valid authorization header (pre hashed payload)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var options = { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' };
+ options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field;
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
+ done();
+ });
+
+ it('errors on missing uri', function (done) {
+
+ var header = Hawk.client.header('', 'POST');
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on invalid uri', function (done) {
+
+ var header = Hawk.client.header(4, 'POST');
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on missing method', function (done) {
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', '');
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on invalid method', function (done) {
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 5);
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on missing options', function (done) {
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST');
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid argument type');
+ done();
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ var credentials = {
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid credential object');
+ done();
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid credential object');
+ done();
+ });
+
+ it('errors on invalid credentials', function (done) {
+
+ var credentials = {
+ id: '123456',
+ algorithm: 'sha256'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Invalid credential object');
+ done();
+ });
+
+ it('errors on invalid algorithm', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
+ };
+
+ var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 });
+ expect(header.field).to.equal('');
+ expect(header.err).to.equal('Unknown algorithm');
+ done();
+ });
+ });
+
+ describe('#authenticate', function () {
+
+ it('returns false on invalid header', function (done) {
+
+ var res = {
+ headers: {
+ 'server-authorization': 'Hawk mac="abc", bad="xyz"'
+ }
+ };
+
+ expect(Hawk.client.authenticate(res, {})).to.equal(false);
+ done();
+ });
+
+ it('returns false on invalid mac', function (done) {
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain',
+ 'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
+ }
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1362336900',
+ nonce: 'eb5S_L',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ app: undefined,
+ dlg: undefined,
+ mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
+ id: '123456'
+ };
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(false);
+ done();
+ });
+
+ it('returns true on ignoring hash', function (done) {
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain',
+ 'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
+ }
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1362336900',
+ nonce: 'eb5S_L',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ app: undefined,
+ dlg: undefined,
+ mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
+ id: '123456'
+ };
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+
+ it('fails on invalid WWW-Authenticate header format', function (done) {
+
+ var header = 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"';
+ expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(false);
+ done();
+ });
+
+ it('fails on invalid WWW-Authenticate header format', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var header = 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"';
+ expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, credentials)).to.equal(false);
+ done();
+ });
+
+ it('skips tsm validation when missing ts', function (done) {
+
+ var header = 'Hawk error="Stale timestamp"';
+ expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(true);
+ done();
+ });
+ });
+
+ describe('#message', function () {
+
+ it('generates authorization', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.exist();
+ expect(auth.ts).to.equal(1353809207);
+ expect(auth.nonce).to.equal('abc123');
+ done();
+ });
+
+ it('errors on invalid host', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message(5, 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on invalid port', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', '80', 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on missing host', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 0, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on null message', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, null, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on missing message', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, undefined, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on invalid message', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, 5, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on missing options', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, 'I am the boodyman');
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ var credentials = {
+ key: '2983d45yun89q',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ algorithm: 'sha1'
+ };
+
+ var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+ });
+});
--- /dev/null
+// Load modules
+
+var Code = require('code');
+var Hawk = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ describe('Crypto', function () {
+
+ describe('#generateNormalizedString', function () {
+
+ it('should return a valid normalized string', function (done) {
+
+ expect(Hawk.crypto.generateNormalizedString('header', {
+ credentials: {
+ key: 'dasdfasdf',
+ algorithm: 'sha256'
+ },
+ ts: 1357747017,
+ nonce: 'k3k4j5',
+ method: 'GET',
+ resource: '/resource/something',
+ host: 'example.com',
+ port: 8080
+ })).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\n\n\n');
+
+ done();
+ });
+
+ it('should return a valid normalized string (ext)', function (done) {
+
+ expect(Hawk.crypto.generateNormalizedString('header', {
+ credentials: {
+ key: 'dasdfasdf',
+ algorithm: 'sha256'
+ },
+ ts: 1357747017,
+ nonce: 'k3k4j5',
+ method: 'GET',
+ resource: '/resource/something',
+ host: 'example.com',
+ port: 8080,
+ ext: 'this is some app data'
+ })).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\n\nthis is some app data\n');
+
+ done();
+ });
+
+ it('should return a valid normalized string (payload + ext)', function (done) {
+
+ expect(Hawk.crypto.generateNormalizedString('header', {
+ credentials: {
+ key: 'dasdfasdf',
+ algorithm: 'sha256'
+ },
+ ts: 1357747017,
+ nonce: 'k3k4j5',
+ method: 'GET',
+ resource: '/resource/something',
+ host: 'example.com',
+ port: 8080,
+ hash: 'U4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=',
+ ext: 'this is some app data'
+ })).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\nU4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=\nthis is some app data\n');
+
+ done();
+ });
+ });
+ });
+});
+
--- /dev/null
+// Load modules
+
+var Url = require('url');
+var Code = require('code');
+var Hawk = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ id: id,
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: (id === '1' ? 'sha1' : 'sha256'),
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ it('generates a header then successfully parse it (configuration)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header(Url.parse('http://example.com:8080/resource/4?filter=a'), req.method, { credentials: credentials, ext: 'some-app-data' }).field;
+ expect(req.authorization).to.exist();
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (node request)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (absolute request uri)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: 'http://example.com:8080/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (no server header options)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then fails to parse it (missing server header hash)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:8080',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ var payload = 'some not so random text';
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
+ req.headers.authorization = reqHeader.field;
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
+
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ }
+ };
+
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
+ expect(res.headers['server-authorization']).to.exist();
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(false);
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (with hash)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it then validate payload', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(Hawk.server.authenticatePayload('hola!', credentials, artifacts)).to.be.true();
+ expect(Hawk.server.authenticatePayload('hello!', credentials, artifacts)).to.be.false();
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parses and validates payload', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, { payload: 'hola!' }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (app)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(artifacts.app).to.equal('asd23ased');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then successfully parse it (app, dlg)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(artifacts.ext).to.equal('some-app-data');
+ expect(artifacts.app).to.equal('asd23ased');
+ expect(artifacts.dlg).to.equal('23434szr3q4d');
+ done();
+ });
+ });
+ });
+
+ it('generates a header then fail authentication due to bad hash', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
+ Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad payload hash');
+ done();
+ });
+ });
+ });
+
+ it('generates a header for one resource then fail to authenticate another', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
+ req.url = '/something/else';
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(credentials).to.exist();
+ done();
+ });
+ });
+ });
+});
--- /dev/null
+// Load modules
+
+var Url = require('url');
+var Code = require('code');
+var Hawk = require('../lib');
+var Hoek = require('hoek');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ id: id,
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: (id === '1' ? 'sha1' : 'sha256'),
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ it('should generate an authorization then successfully parse it', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on mismatching host', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example1.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Bad mac');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on stale timestamp', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { localtimeOffsetMsec: 100000 }, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Stale timestamp');
+ done();
+ });
+ });
+ });
+
+ it('overrides timestampSkewSec', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials, localtimeOffsetMsec: 100000 });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { timestampSkewSec: 500 }, function (err, credentials) {
+
+ expect(err).to.not.exist();
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on invalid authorization', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+ delete auth.id;
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid authorization');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on bad hash', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message1', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Bad message hash');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on nonce error', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { nonceFunc: function (nonce, ts, callback) { callback (new Error('kaboom')); } }, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid nonce');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on credentials error', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ var errFunc = function (id, callback) {
+
+ callback(new Error('kablooey'));
+ };
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('kablooey');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on missing credentials', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ var errFunc = function (id, callback) {
+
+ callback();
+ };
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Unknown credentials');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on invalid credentials', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ var errFunc = function (id, callback) {
+
+ callback(null, {});
+ };
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid credentials');
+ done();
+ });
+ });
+ });
+
+ it('should fail authorization on invalid credentials algorithm', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ expect(auth).to.exist();
+
+ var errFunc = function (id, callback) {
+
+ callback(null, { key: '123', algorithm: '456' });
+ };
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Unknown algorithm');
+ done();
+ });
+ });
+ });
+
+ it('should fail on missing host', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message(null, 8080, 'some message', { credentials: credentials });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+
+ it('should fail on missing credentials', function (done) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', {});
+ expect(auth).to.not.exist();
+ done();
+ });
+
+ it('should fail on invalid algorithm', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var creds = Hoek.clone(credentials);
+ creds.algorithm = 'blah';
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: creds });
+ expect(auth).to.not.exist();
+ done();
+ });
+ });
+});
--- /dev/null
+// Load modules
+
+var Code = require('code');
+var Hawk = require('../lib');
+var Hoek = require('hoek');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ describe('README', function () {
+
+ describe('core', function () {
+
+ var credentials = {
+ id: 'dh37fgj492je',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256'
+ };
+
+ var options = {
+ credentials: credentials,
+ timestamp: 1353832234,
+ nonce: 'j4h3g2',
+ ext: 'some-app-ext-data'
+ };
+
+ it('should generate a header protocol example', function (done) {
+
+ var header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', options).field;
+
+ expect(header).to.equal('Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="');
+ done();
+ });
+
+ it('should generate a normalized string protocol example', function (done) {
+
+ var normalized = Hawk.crypto.generateNormalizedString('header', {
+ credentials: credentials,
+ ts: options.timestamp,
+ nonce: options.nonce,
+ method: 'GET',
+ resource: '/resource?a=1&b=2',
+ host: 'example.com',
+ port: 8000,
+ ext: options.ext
+ });
+
+ expect(normalized).to.equal('hawk.1.header\n1353832234\nj4h3g2\nGET\n/resource?a=1&b=2\nexample.com\n8000\n\nsome-app-ext-data\n');
+ done();
+ });
+
+ var payloadOptions = Hoek.clone(options);
+ payloadOptions.payload = 'Thank you for flying Hawk';
+ payloadOptions.contentType = 'text/plain';
+
+ it('should generate a header protocol example (with payload)', function (done) {
+
+ var header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'POST', payloadOptions).field;
+
+ expect(header).to.equal('Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="');
+ done();
+ });
+
+ it('should generate a normalized string protocol example (with payload)', function (done) {
+
+ var normalized = Hawk.crypto.generateNormalizedString('header', {
+ credentials: credentials,
+ ts: options.timestamp,
+ nonce: options.nonce,
+ method: 'POST',
+ resource: '/resource?a=1&b=2',
+ host: 'example.com',
+ port: 8000,
+ hash: Hawk.crypto.calculatePayloadHash(payloadOptions.payload, credentials.algorithm, payloadOptions.contentType),
+ ext: options.ext
+ });
+
+ expect(normalized).to.equal('hawk.1.header\n1353832234\nj4h3g2\nPOST\n/resource?a=1&b=2\nexample.com\n8000\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\nsome-app-ext-data\n');
+ done();
+ });
+ });
+ });
+});
+
--- /dev/null
+// Load modules
+
+var Url = require('url');
+var Code = require('code');
+var Hawk = require('../lib');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ describe('server', function () {
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ id: id,
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: (id === '1' ? 'sha1' : 'sha256'),
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ describe('#authenticate', function () {
+
+ it('parses a valid authentication header (sha1)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="1", ts="1353788437", nonce="k3j4h2", mac="zy79QQ5/EYFmQqutVnYb73gAc/U=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+
+ it('parses a valid authentication header (sha256)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/1?b=1&a=2',
+ host: 'example.com',
+ port: 8000,
+ authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="m8r1rHbXN6NgO+KIIhjO7sFRyd78RNGVUwehe8Cp2dU=", ext="some-app-data"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353832234000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+
+ it('parses a valid authentication header (host override)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example1.com:8080',
+ authorization: 'Hawk id="1", ts="1353788437", nonce="k3j4h2", mac="zy79QQ5/EYFmQqutVnYb73gAc/U=", ext="hello"'
+ }
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { host: 'example.com', localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+
+ it('parses a valid authentication header (host port override)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example1.com:80',
+ authorization: 'Hawk id="1", ts="1353788437", nonce="k3j4h2", mac="zy79QQ5/EYFmQqutVnYb73gAc/U=", ext="hello"'
+ }
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { host: 'example.com', port: 8080, localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+
+ it('parses a valid authentication header (POST with payload)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123456", ts="1357926341", nonce="1AwuJD", hash="qAiXIVv+yjDATneWxZP2YCTa9aHRgQdnH9b3Wc+o3dg=", ext="some-app-data", mac="UeYcj5UoTVaAWXNvJfLVia7kU3VabxCqrccXP8sUGC4="'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1357926341000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+
+ it('errors on missing hash', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/1?b=1&a=2',
+ host: 'example.com',
+ port: 8000,
+ authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="m8r1rHbXN6NgO+KIIhjO7sFRyd78RNGVUwehe8Cp2dU=", ext="some-app-data"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { payload: 'body', localtimeOffsetMsec: 1353832234000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing required payload hash');
+ done();
+ });
+ });
+
+ it('errors on a stale timestamp', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123456", ts="1362337299", nonce="UzmxSs", ext="some-app-data", mac="wnNUxchvvryMH2RxckTdZ/gY3ijzvccx4keVvELC61w="'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Stale timestamp');
+ var header = err.output.headers['WWW-Authenticate'];
+ var ts = header.match(/^Hawk ts\=\"(\d+)\"\, tsm\=\"([^\"]+)\"\, error=\"Stale timestamp\"$/);
+ var now = Hawk.utils.now();
+ expect(parseInt(ts[1], 10) * 1000).to.be.within(now - 1000, now + 1000);
+
+ var res = {
+ headers: {
+ 'www-authenticate': header
+ }
+ };
+
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+ });
+
+ it('errors on a replay', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="bXx7a7p1h9QYQNZ8x7QhvDQym8ACgab4m3lVSFn4DBw=", ext="hello"'
+ };
+
+ var memoryCache = {};
+ var options = {
+ localtimeOffsetMsec: 1353788437000 - Hawk.utils.now(),
+ nonceFunc: function (nonce, ts, callback) {
+
+ if (memoryCache[nonce]) {
+ return callback(new Error());
+ }
+
+ memoryCache[nonce] = true;
+ return callback();
+ }
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, options, function (err, credentials, artifacts) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+
+ Hawk.server.authenticate(req, credentialsFunc, options, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid nonce');
+ done();
+ });
+ });
+ });
+
+ it('errors on an invalid authentication header: wrong scheme', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Basic asdasdasdasd'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.not.exist();
+ done();
+ });
+ });
+
+ it('errors on an invalid authentication header: no scheme', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: '!@#'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid header syntax');
+ done();
+ });
+ });
+
+ it('errors on an missing authorization header', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.isMissing).to.equal(true);
+ done();
+ });
+ });
+
+ it('errors on an missing host header', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ headers: {
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ }
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid Host header');
+ done();
+ });
+ });
+
+ it('errors on an missing authorization attribute (id)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing attributes');
+ done();
+ });
+ });
+
+ it('errors on an missing authorization attribute (ts)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing attributes');
+ done();
+ });
+ });
+
+ it('errors on an missing authorization attribute (nonce)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing attributes');
+ done();
+ });
+ });
+
+ it('errors on an missing authorization attribute (mac)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing attributes');
+ done();
+ });
+ });
+
+ it('errors on an unknown authorization attribute', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", x="3", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Unknown attribute: x');
+ done();
+ });
+ });
+
+ it('errors on an bad authorization header format', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123\\", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad header format');
+ done();
+ });
+ });
+
+ it('errors on an bad authorization attribute value', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="\t", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad attribute value: id');
+ done();
+ });
+ });
+
+ it('errors on an empty authorization attribute value', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad attribute value: id');
+ done();
+ });
+ });
+
+ it('errors on duplicated authorization attribute key', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", id="456", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Duplicate attribute: id');
+ done();
+ });
+ });
+
+ it('errors on an invalid authorization header format', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk'
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid header syntax');
+ done();
+ });
+ });
+
+ it('errors on an bad host header (missing host)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: ':8080',
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ }
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid Host header');
+ done();
+ });
+ });
+
+ it('errors on an bad host header (pad port)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com:something',
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ }
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid Host header');
+ done();
+ });
+ });
+
+ it('errors on credentialsFunc error', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ return callback(new Error('Unknown user'));
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Unknown user');
+ done();
+ });
+ });
+
+ it('errors on credentialsFunc error (with credentials)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ return callback(new Error('Unknown user'), { some: 'value' });
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Unknown user');
+ expect(credentials.some).to.equal('value');
+ done();
+ });
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ return callback(null, null);
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Unknown credentials');
+ done();
+ });
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid credentials');
+ expect(err.output.payload.message).to.equal('An internal server error occurred');
+ done();
+ });
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ id: '23434d3q4d5345d',
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid credentials');
+ expect(err.output.payload.message).to.equal('An internal server error occurred');
+ done();
+ });
+ });
+
+ it('errors on unknown credentials algorithm', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'hmac-sha-0',
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Unknown algorithm');
+ expect(err.output.payload.message).to.equal('An internal server error occurred');
+ done();
+ });
+ });
+
+ it('errors on unknown bad mac', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcU4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
+ };
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ Hawk.server.authenticate(req, credentialsFunc, { localtimeOffsetMsec: 1353788437000 - Hawk.utils.now() }, function (err, credentials, artifacts) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad mac');
+ done();
+ });
+ });
+ });
+
+ describe('#header', function () {
+
+ it('generates header', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('Hawk mac=\"n14wVJK4cOxAytPUMc5bPezQzuJGl5n7MYXhFQgEKsE=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\", ext=\"response-specific\"');
+ done();
+ });
+
+ it('generates header (empty payload)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var header = Hawk.server.header(credentials, artifacts, { payload: '', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('Hawk mac=\"i8/kUBDx0QF+PpCtW860kkV/fa9dbwEoe/FpGUXowf0=\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", ext=\"response-specific\"');
+ done();
+ });
+
+ it('generates header (pre calculated hash)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var options = { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' };
+ options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
+ var header = Hawk.server.header(credentials, artifacts, options);
+ expect(header).to.equal('Hawk mac=\"n14wVJK4cOxAytPUMc5bPezQzuJGl5n7MYXhFQgEKsE=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\", ext=\"response-specific\"');
+ done();
+ });
+
+ it('generates header (null ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: null });
+ expect(header).to.equal('Hawk mac=\"6PrybJTJs20jsgBw5eilXpcytD8kUbaIKNYXL+6g0ns=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\"');
+ done();
+ });
+
+ it('errors on missing artifacts', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var header = Hawk.server.header(credentials, null, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('');
+ done();
+ });
+
+ it('errors on invalid artifacts', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var header = Hawk.server.header(credentials, 5, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('');
+ done();
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var header = Hawk.server.header(null, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('');
+ done();
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('');
+ done();
+ });
+
+ it('errors on invalid algorithm', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'x',
+ user: 'steve'
+ };
+
+ var artifacts = {
+ method: 'POST',
+ host: 'example.com',
+ port: '8080',
+ resource: '/resource/4?filter=a',
+ ts: '1398546787',
+ nonce: 'xUwusx',
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
+ ext: 'some-app-data',
+ mac: 'dvIvMThwi28J61Jc3P0ryAhuKpanU63GXdx6hkmQkJA=',
+ id: '123456'
+ };
+
+ var header = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ expect(header).to.equal('');
+ done();
+ });
+ });
+
+ describe('#authenticateMessage', function () {
+
+ it('errors on invalid authorization (ts)', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ delete auth.ts;
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid authorization');
+ done();
+ });
+ });
+ });
+
+ it('errors on invalid authorization (nonce)', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ delete auth.nonce;
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid authorization');
+ done();
+ });
+ });
+ });
+
+ it('errors on invalid authorization (hash)', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+ delete auth.hash;
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid authorization');
+ done();
+ });
+ });
+ });
+
+ it('errors with credentials', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials });
+
+ Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, function (id, callback) { callback(new Error('something'), { some: 'value' }); }, {}, function (err, credentials) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('something');
+ expect(credentials.some).to.equal('value');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#authenticatePayloadHash', function () {
+
+ it('checks payload hash', function (done) {
+
+ expect(Hawk.server.authenticatePayloadHash('abcdefg', { hash: 'abcdefg' })).to.equal(true);
+ expect(Hawk.server.authenticatePayloadHash('1234567', { hash: 'abcdefg' })).to.equal(false);
+ done();
+ });
+ });
+ });
+});
--- /dev/null
+// Load modules
+
+var Http = require('http');
+var Url = require('url');
+var Code = require('code');
+var Hawk = require('../lib');
+var Hoek = require('hoek');
+var Lab = require('lab');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ describe('Uri', function () {
+
+ var credentialsFunc = function (id, callback) {
+
+ var credentials = {
+ id: id,
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
+ };
+
+ return callback(null, credentials);
+ };
+
+ it('should generate a bewit then successfully authenticate it', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2',
+ host: 'example.com',
+ port: 80
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials, ttlSec: 60 * 60 * 24 * 365 * 100, ext: 'some-app-data' });
+ req.url += '&bewit=' + bewit;
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(attributes.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+ });
+
+ it('should generate a bewit then successfully authenticate it (no ext)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2',
+ host: 'example.com',
+ port: 80
+ };
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials, ttlSec: 60 * 60 * 24 * 365 * 100 });
+ req.url += '&bewit=' + bewit;
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ done();
+ });
+ });
+ });
+
+ it('should successfully authenticate a request (last param)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2&bewit=MTIzNDU2XDQ1MTE0ODQ2MjFcMzFjMmNkbUJFd1NJRVZDOVkva1NFb2c3d3YrdEVNWjZ3RXNmOGNHU2FXQT1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(attributes.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+
+ it('should successfully authenticate a request (first param)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MTE0ODQ2MjFcMzFjMmNkbUJFd1NJRVZDOVkva1NFb2c3d3YrdEVNWjZ3RXNmOGNHU2FXQT1cc29tZS1hcHAtZGF0YQ&a=1&b=2',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(attributes.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+
+ it('should successfully authenticate a request (only param)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MTE0ODQ2NDFcZm1CdkNWT3MvcElOTUUxSTIwbWhrejQ3UnBwTmo4Y1VrSHpQd3Q5OXJ1cz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.not.exist();
+ expect(credentials.user).to.equal('steve');
+ expect(attributes.ext).to.equal('some-app-data');
+ done();
+ });
+ });
+
+ it('should fail on multiple authentication', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MTE0ODQ2NDFcZm1CdkNWT3MvcElOTUUxSTIwbWhrejQ3UnBwTmo4Y1VrSHpQd3Q5OXJ1cz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080,
+ authorization: 'Basic asdasdasdasd'
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Multiple authentications');
+ done();
+ });
+ });
+
+ it('should fail on method other than GET', function (done) {
+
+ credentialsFunc('123456', function (err, credentials) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ host: 'example.com',
+ port: 8080
+ };
+
+ var exp = Math.floor(Hawk.utils.now() / 1000) + 60;
+ var ext = 'some-app-data';
+ var mac = Hawk.crypto.calculateMac('bewit', credentials, {
+ timestamp: exp,
+ nonce: '',
+ method: req.method,
+ resource: req.url,
+ host: req.host,
+ port: req.port,
+ ext: ext
+ });
+
+ var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + ext;
+
+ req.url += '&bewit=' + Hoek.base64urlEncode(bewit);
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid method');
+ done();
+ });
+ });
+ });
+
+ it('should fail on invalid host header', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ headers: {
+ host: 'example.com:something'
+ }
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid Host header');
+ done();
+ });
+ });
+
+ it('should fail on empty bewit', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Empty bewit');
+ expect(err.isMissing).to.not.exist();
+ done();
+ });
+ });
+
+ it('should fail on invalid bewit', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=*',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid bewit encoding');
+ expect(err.isMissing).to.not.exist();
+ done();
+ });
+ });
+
+ it('should fail on missing bewit', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.not.exist();
+ expect(err.isMissing).to.equal(true);
+ done();
+ });
+ });
+
+ it('should fail on invalid bewit structure', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=abc',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Invalid bewit structure');
+ done();
+ });
+ });
+
+ it('should fail on empty bewit attribute', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=YVxcY1xk',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing bewit attributes');
+ done();
+ });
+ });
+
+ it('should fail on missing bewit id attribute', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=XDQ1NTIxNDc2MjJcK0JFbFhQMXhuWjcvd1Nrbm1ldGhlZm5vUTNHVjZNSlFVRHk4NWpTZVJ4VT1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Missing bewit attributes');
+ done();
+ });
+ });
+
+ it('should fail on expired access', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?a=1&b=2&bewit=MTIzNDU2XDEzNTY0MTg1ODNcWk1wZlMwWU5KNHV0WHpOMmRucTRydEk3NXNXTjFjeWVITTcrL0tNZFdVQT1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Access expired');
+ done();
+ });
+ });
+
+ it('should fail on credentials function error', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, function (id, callback) { callback(Hawk.error.badRequest('Boom')); }, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Boom');
+ done();
+ });
+ });
+
+ it('should fail on credentials function error with credentials', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, function (id, callback) { callback(Hawk.error.badRequest('Boom'), { some: 'value' }); }, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Boom');
+ expect(credentials.some).to.equal('value');
+ done();
+ });
+ });
+
+ it('should fail on null credentials function response', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, function (id, callback) { callback(null, null); }, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Unknown credentials');
+ done();
+ });
+ });
+
+ it('should fail on invalid credentials function response', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, function (id, callback) { callback(null, {}); }, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Invalid credentials');
+ done();
+ });
+ });
+
+ it('should fail on invalid credentials function response (unknown algorithm)', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, function (id, callback) { callback(null, { key: 'xxx', algorithm: 'xxx' }); }, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.message).to.equal('Unknown algorithm');
+ done();
+ });
+ });
+
+ it('should fail on expired access', function (done) {
+
+ var req = {
+ method: 'GET',
+ url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
+ host: 'example.com',
+ port: 8080
+ };
+
+ Hawk.uri.authenticate(req, function (id, callback) { callback(null, { key: 'xxx', algorithm: 'sha256' }); }, {}, function (err, credentials, attributes) {
+
+ expect(err).to.exist();
+ expect(err.output.payload.message).to.equal('Bad mac');
+ done();
+ });
+ });
+ });
+
+ describe('#getBewit', function () {
+
+ it('returns a valid bewit value', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
+ done();
+ });
+
+ it('returns a valid bewit value (explicit port)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com:8080/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcaFpiSjNQMmNLRW80a3kwQzhqa1pBa1J5Q1p1ZWc0V1NOYnhWN3ZxM3hIVT1ceGFuZHlhbmR6');
+ done();
+ });
+
+ it('returns a valid bewit value (null ext)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: null });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcSUdZbUxnSXFMckNlOEN4dktQczRKbFdJQStValdKSm91d2dBUmlWaENBZz1c');
+ done();
+ });
+
+ it('returns a valid bewit value (parsed uri)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit(Url.parse('https://example.com/somewhere/over/the/rainbow'), { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
+ done();
+ });
+
+ it('errors on invalid options', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', 4);
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on missing uri', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid uri', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit(5, { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid credentials (id)', function (done) {
+
+ var credentials = {
+ key: '2983d45yun89q',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on missing credentials', function (done) {
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid credentials (key)', function (done) {
+
+ var credentials = {
+ id: '123456',
+ algorithm: 'sha256'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on invalid algorithm', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, ext: 'xandyandz' });
+ expect(bewit).to.equal('');
+ done();
+ });
+
+ it('errors on missing options', function (done) {
+
+ var credentials = {
+ id: '123456',
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
+ };
+
+ var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow');
+ expect(bewit).to.equal('');
+ done();
+ });
+ });
+});
+
--- /dev/null
+// Load modules
+
+var Code = require('code');
+var Hawk = require('../lib');
+var Lab = require('lab');
+var Package = require('../package.json');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var describe = lab.experiment;
+var it = lab.test;
+var expect = Code.expect;
+
+
+describe('Hawk', function () {
+
+ describe('Utils', function () {
+
+ describe('#parseHost', function () {
+
+ it('returns port 80 for non tls node request', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com',
+ 'content-type': 'text/plain;x=y'
+ }
+ };
+
+ expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(80);
+ done();
+ });
+
+ it('returns port 443 for non tls node request', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: 'example.com',
+ 'content-type': 'text/plain;x=y'
+ },
+ connection: {
+ encrypted: true
+ }
+ };
+
+ expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
+ done();
+ });
+
+ it('returns port 443 for non tls node request (IPv6)', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: '[123:123:123]',
+ 'content-type': 'text/plain;x=y'
+ },
+ connection: {
+ encrypted: true
+ }
+ };
+
+ expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
+ done();
+ });
+
+ it('parses IPv6 headers', function (done) {
+
+ var req = {
+ method: 'POST',
+ url: '/resource/4?filter=a',
+ headers: {
+ host: '[123:123:123]:8000',
+ 'content-type': 'text/plain;x=y'
+ },
+ connection: {
+ encrypted: true
+ }
+ };
+
+ var host = Hawk.utils.parseHost(req, 'Host');
+ expect(host.port).to.equal('8000');
+ expect(host.name).to.equal('[123:123:123]');
+ done();
+ });
+ });
+
+ describe('#version', function () {
+
+ it('returns the correct package version number', function (done) {
+
+ expect(Hawk.utils.version()).to.equal(Package.version);
+ done();
+ });
+ });
+
+ describe('#unauthorized', function () {
+
+ it('returns a hawk 401', function (done) {
+
+ expect(Hawk.utils.unauthorized('kaboom').output.headers['WWW-Authenticate']).to.equal('Hawk error="kaboom"');
+ done();
+ });
+ });
+ });
+});
+
+
--- /dev/null
+((nil . ((indent-tabs-mode . nil)
+ (tab-width . 8)
+ (fill-column . 80)))
+ (js-mode . ((js-indent-level . 2)
+ (indent-tabs-mode . nil)
+ )))
\ No newline at end of file
--- /dev/null
+.gitmodules
+deps
+docs
+Makefile
+node_modules
+test
+tools
\ No newline at end of file
--- /dev/null
+Copyright Joyent, Inc. 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.
--- /dev/null
+# node-http-signature
+
+node-http-signature is a node.js library that has client and server components
+for Joyent's [HTTP Signature Scheme](http_signing.md).
+
+## Usage
+
+Note the example below signs a request with the same key/cert used to start an
+HTTP server. This is almost certainly not what you actually want, but is just
+used to illustrate the API calls; you will need to provide your own key
+management in addition to this library.
+
+### Client
+
+```js
+var fs = require('fs');
+var https = require('https');
+var httpSignature = require('http-signature');
+
+var key = fs.readFileSync('./key.pem', 'ascii');
+
+var options = {
+ host: 'localhost',
+ port: 8443,
+ path: '/',
+ method: 'GET',
+ headers: {}
+};
+
+// Adds a 'Date' header in, signs it, and adds the
+// 'Authorization' header in.
+var req = https.request(options, function(res) {
+ console.log(res.statusCode);
+});
+
+
+httpSignature.sign(req, {
+ key: key,
+ keyId: './cert.pem'
+});
+
+req.end();
+```
+
+### Server
+
+```js
+var fs = require('fs');
+var https = require('https');
+var httpSignature = require('http-signature');
+
+var options = {
+ key: fs.readFileSync('./key.pem'),
+ cert: fs.readFileSync('./cert.pem')
+};
+
+https.createServer(options, function (req, res) {
+ var rc = 200;
+ var parsed = httpSignature.parseRequest(req);
+ var pub = fs.readFileSync(parsed.keyId, 'ascii');
+ if (!httpSignature.verifySignature(parsed, pub))
+ rc = 401;
+
+ res.writeHead(rc);
+ res.end();
+}).listen(8443);
+```
+
+## Installation
+
+ npm install http-signature
+
+## License
+
+MIT.
+
+## Bugs
+
+See <https://github.com/joyent/node-http-signature/issues>.
--- /dev/null
+# Abstract
+
+This document describes a way to add origin authentication, message integrity,
+and replay resistance to HTTP REST requests. It is intended to be used over
+the HTTPS protocol.
+
+# Copyright Notice
+
+Copyright (c) 2011 Joyent, Inc. and the persons identified as document authors.
+All rights reserved.
+
+Code Components extracted from this document must include MIT License text.
+
+# Introduction
+
+This protocol is intended to provide a standard way for clients to sign HTTP
+requests. RFC2617 (HTTP Authentication) defines Basic and Digest authentication
+mechanisms, and RFC5246 (TLS 1.2) defines client-auth, both of which are widely
+employed on the Internet today. However, it is common place that the burdens of
+PKI prevent web service operators from deploying that methodology, and so many
+fall back to Basic authentication, which has poor security characteristics.
+
+Additionally, OAuth provides a fully-specified alternative for authorization
+of web service requests, but is not (always) ideal for machine to machine
+communication, as the key acquisition steps (generally) imply a fixed
+infrastructure that may not make sense to a service provider (e.g., symmetric
+keys).
+
+Several web service providers have invented their own schemes for signing
+HTTP requests, but to date, none have been placed in the public domain as a
+standard. This document serves that purpose. There are no techniques in this
+proposal that are novel beyond previous art, however, this aims to be a simple
+mechanism for signing these requests.
+
+# Signature Authentication Scheme
+
+The "signature" authentication scheme is based on the model that the client must
+authenticate itself with a digital signature produced by either a private
+asymmetric key (e.g., RSA) or a shared symmetric key (e.g., HMAC). The scheme
+is parameterized enough such that it is not bound to any particular key type or
+signing algorithm. However, it does explicitly assume that clients can send an
+HTTP `Date` header.
+
+## Authorization Header
+
+The client is expected to send an Authorization header (as defined in RFC 2617)
+with the following parameterization:
+
+ credentials := "Signature" params
+ params := 1#(keyId | algorithm | [headers] | [ext] | signature)
+ digitalSignature := plain-string
+
+ keyId := "keyId" "=" <"> plain-string <">
+ algorithm := "algorithm" "=" <"> plain-string <">
+ headers := "headers" "=" <"> 1#headers-value <">
+ ext := "ext" "=" <"> plain-string <">
+ signature := "signature" "=" <"> plain-string <">
+
+ headers-value := plain-string
+ plain-string = 1*( %x20-21 / %x23-5B / %x5D-7E )
+
+### Signature Parameters
+
+#### keyId
+
+REQUIRED. The `keyId` field is an opaque string that the server can use to look
+up the component they need to validate the signature. It could be an SSH key
+fingerprint, an LDAP DN, etc. Management of keys and assignment of `keyId` is
+out of scope for this document.
+
+#### algorithm
+
+REQUIRED. The `algorithm` parameter is used if the client and server agree on a
+non-standard digital signature algorithm. The full list of supported signature
+mechanisms is listed below.
+
+#### headers
+
+OPTIONAL. The `headers` parameter is used to specify the list of HTTP headers
+used to sign the request. If specified, it should be a quoted list of HTTP
+header names, separated by a single space character. By default, only one
+HTTP header is signed, which is the `Date` header. Note that the list MUST be
+specified in the order the values are concatenated together during signing. To
+include the HTTP request line in the signature calculation, use the special
+`request-line` value. While this is overloading the definition of `headers` in
+HTTP linguism, the request-line is defined in RFC 2616, and as the outlier from
+headers in useful signature calculation, it is deemed simpler to simply use
+`request-line` than to add a separate parameter for it.
+
+#### extensions
+
+OPTIONAL. The `extensions` parameter is used to include additional information
+which is covered by the request. The content and format of the string is out of
+scope for this document, and expected to be specified by implementors.
+
+#### signature
+
+REQUIRED. The `signature` parameter is a `Base64` encoded digital signature
+generated by the client. The client uses the `algorithm` and `headers` request
+parameters to form a canonicalized `signing string`. This `signing string` is
+then signed with the key associated with `keyId` and the algorithm
+corresponding to `algorithm`. The `signature` parameter is then set to the
+`Base64` encoding of the signature.
+
+### Signing String Composition
+
+In order to generate the string that is signed with a key, the client MUST take
+the values of each HTTP header specified by `headers` in the order they appear.
+
+1. If the header name is not `request-line` then append the lowercased header
+ name followed with an ASCII colon `:` and an ASCII space ` `.
+2. If the header name is `request-line` then append the HTTP request line,
+ otherwise append the header value.
+3. If value is not the last value then append an ASCII newline `\n`. The string
+ MUST NOT include a trailing ASCII newline.
+
+# Example Requests
+
+All requests refer to the following request (body omitted):
+
+ POST /foo HTTP/1.1
+ Host: example.org
+ Date: Tue, 07 Jun 2011 20:51:35 GMT
+ Content-Type: application/json
+ Content-MD5: h0auK8hnYJKmHTLhKtMTkQ==
+ Content-Length: 123
+
+The "rsa-key-1" keyId refers to a private key known to the client and a public
+key known to the server. The "hmac-key-1" keyId refers to key known to the
+client and server.
+
+## Default parameterization
+
+The authorization header and signature would be generated as:
+
+ Authorization: Signature keyId="rsa-key-1",algorithm="rsa-sha256",signature="Base64(RSA-SHA256(signing string))"
+
+The client would compose the signing string as:
+
+ date: Tue, 07 Jun 2011 20:51:35 GMT
+
+## Header List
+
+The authorization header and signature would be generated as:
+
+ Authorization: Signature keyId="rsa-key-1",algorithm="rsa-sha256",headers="request-line date content-type content-md5",signature="Base64(RSA-SHA256(signing string))"
+
+The client would compose the signing string as (`+ "\n"` inserted for
+readability):
+
+ POST /foo HTTP/1.1 + "\n"
+ date: Tue, 07 Jun 2011 20:51:35 GMT + "\n"
+ content-type: application/json + "\n"
+ content-md5: h0auK8hnYJKmHTLhKtMTkQ==
+
+## Algorithm
+
+The authorization header and signature would be generated as:
+
+ Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
+
+The client would compose the signing string as:
+
+ date: Tue, 07 Jun 2011 20:51:35 GMT
+
+# Signing Algorithms
+
+Currently supported algorithm names are:
+
+* rsa-sha1
+* rsa-sha256
+* rsa-sha512
+* dsa-sha1
+* hmac-sha1
+* hmac-sha256
+* hmac-sha512
+
+# Security Considerations
+
+## Default Parameters
+
+Note the default parameterization of the `Signature` scheme is only safe if all
+requests are carried over a secure transport (i.e., TLS). Sending the default
+scheme over a non-secure transport will leave the request vulnerable to
+spoofing, tampering, replay/repudiation, and integrity violations (if using the
+STRIDE threat-modeling methodology).
+
+## Insecure Transports
+
+If sending the request over plain HTTP, service providers SHOULD require clients
+to sign ALL HTTP headers, and the `request-line`. Additionally, service
+providers SHOULD require `Content-MD5` calculations to be performed to ensure
+against any tampering from clients.
+
+## Nonces
+
+Nonces are out of scope for this document simply because many service providers
+fail to implement them correctly, or do not adopt security specifications
+because of the infrastructure complexity. Given the `header` parameterization,
+a service provider is fully enabled to add nonce semantics into this scheme by
+using something like an `x-request-nonce` header, and ensuring it is signed
+with the `Date` header.
+
+## Clock Skew
+
+As the default scheme is to sign the `Date` header, service providers SHOULD
+protect against logged replay attacks by enforcing a clock skew. The server
+SHOULD be synchronized with NTP, and the recommendation in this specification
+is to allow 300s of clock skew (in either direction).
+
+## Required Headers to Sign
+
+It is out of scope for this document to dictate what headers a service provider
+will want to enforce, but service providers SHOULD at minimum include the
+`Date` header.
+
+# References
+
+## Normative References
+
+* [RFC2616] Hypertext Transfer Protocol -- HTTP/1.1
+* [RFC2617] HTTP Authentication: Basic and Digest Access Authentication
+* [RFC5246] The Transport Layer Security (TLS) Protocol Version 1.2
+
+## Informative References
+
+ Name: Mark Cavage (editor)
+ Company: Joyent, Inc.
+ Email: mark.cavage@joyent.com
+ URI: http://www.joyent.com
+
+# Appendix A - Test Values
+
+The following test data uses the RSA (2048b) keys, which we will refer
+to as `keyId=Test` in the following samples:
+
+ -----BEGIN PUBLIC KEY-----
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
+ 6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
+ Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
+ oYi+1hqp1fIekaxsyQIDAQAB
+ -----END PUBLIC KEY-----
+
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
+ NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
+ UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
+ AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
+ QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
+ kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
+ f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
+ 412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
+ mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
+ kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
+ gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
+ G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
+ 7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
+ -----END RSA PRIVATE KEY-----
+
+And all examples use this request:
+
+ POST /foo?param=value&pet=dog HTTP/1.1
+ Host: example.com
+ Date: Thu, 05 Jan 2012 21:31:40 GMT
+ Content-Type: application/json
+ Content-MD5: Sd/dVLAcvNLSq16eXua5uQ==
+ Content-Length: 18
+
+ {"hello": "world"}
+
+### Default
+
+The string to sign would be:
+
+ date: Thu, 05 Jan 2012 21:31:40 GMT
+
+The Authorization header would be:
+
+ Authorization: Signature keyId="Test",algorithm="rsa-sha256",signature="ATp0r26dbMIxOopqw0OfABDT7CKMIoENumuruOtarj8n/97Q3htHFYpH8yOSQk3Z5zh8UxUym6FYTb5+A0Nz3NRsXJibnYi7brE/4tx5But9kkFGzG+xpUmimN4c3TMN7OFH//+r8hBf7BT9/GmHDUVZT2JzWGLZES2xDOUuMtA="
+
+### All Headers
+
+Parameterized to include all headers, the string to sign would be (`+ "\n"`
+inserted for readability):
+
+ POST /foo?param=value&pet=dog HTTP/1.1 + "\n"
+ host: example.com + "\n"
+ date: Thu, 05 Jan 2012 21:31:40 GMT + "\n"
+ content-type: application/json + "\n"
+ content-md5: Sd/dVLAcvNLSq16eXua5uQ== + "\n"
+ content-length: 18
+
+The Authorization header would be:
+
+ Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="request-line host date content-type content-md5 content-length",signature="H/AaTDkJvLELy4i1RujnKlS6dm8QWiJvEpn9cKRMi49kKF+mohZ15z1r+mF+XiKS5kOOscyS83olfBtsVhYjPg2Ei3/D9D4Mvb7bFm9IaLJgYTFFuQCghrKQQFPiqJN320emjHxFowpIm1BkstnEU7lktH/XdXVBo8a6Uteiztw="
+
--- /dev/null
+// Copyright 2011 Joyent, Inc. All rights reserved.
+
+var parser = require('./parser');
+var signer = require('./signer');
+var verify = require('./verify');
+var util = require('./util');
+
+
+
+///--- API
+
+module.exports = {
+
+ parse: parser.parseRequest,
+ parseRequest: parser.parseRequest,
+
+ sign: signer.signRequest,
+ signRequest: signer.signRequest,
+
+ sshKeyToPEM: util.sshKeyToPEM,
+ sshKeyFingerprint: util.fingerprint,
+ pemToRsaSSHKey: util.pemToRsaSSHKey,
+
+ verify: verify.verifySignature,
+ verifySignature: verify.verifySignature
+};
--- /dev/null
+// Copyright 2012 Joyent, Inc. All rights reserved.
+
+var assert = require('assert-plus');
+var util = require('util');
+
+
+
+///--- Globals
+
+var Algorithms = {
+ 'rsa-sha1': true,
+ 'rsa-sha256': true,
+ 'rsa-sha512': true,
+ 'dsa-sha1': true,
+ 'hmac-sha1': true,
+ 'hmac-sha256': true,
+ 'hmac-sha512': true
+};
+
+var State = {
+ New: 0,
+ Params: 1
+};
+
+var ParamsState = {
+ Name: 0,
+ Quote: 1,
+ Value: 2,
+ Comma: 3
+};
+
+
+
+///--- Specific Errors
+
+function HttpSignatureError(message, caller) {
+ if (Error.captureStackTrace)
+ Error.captureStackTrace(this, caller || HttpSignatureError);
+
+ this.message = message;
+ this.name = caller.name;
+}
+util.inherits(HttpSignatureError, Error);
+
+function ExpiredRequestError(message) {
+ HttpSignatureError.call(this, message, ExpiredRequestError);
+}
+util.inherits(ExpiredRequestError, HttpSignatureError);
+
+
+function InvalidHeaderError(message) {
+ HttpSignatureError.call(this, message, InvalidHeaderError);
+}
+util.inherits(InvalidHeaderError, HttpSignatureError);
+
+
+function InvalidParamsError(message) {
+ HttpSignatureError.call(this, message, InvalidParamsError);
+}
+util.inherits(InvalidParamsError, HttpSignatureError);
+
+
+function MissingHeaderError(message) {
+ HttpSignatureError.call(this, message, MissingHeaderError);
+}
+util.inherits(MissingHeaderError, HttpSignatureError);
+
+
+
+///--- Exported API
+
+module.exports = {
+
+ /**
+ * Parses the 'Authorization' header out of an http.ServerRequest object.
+ *
+ * Note that this API will fully validate the Authorization header, and throw
+ * on any error. It will not however check the signature, or the keyId format
+ * as those are specific to your environment. You can use the options object
+ * to pass in extra constraints.
+ *
+ * As a response object you can expect this:
+ *
+ * {
+ * "scheme": "Signature",
+ * "params": {
+ * "keyId": "foo",
+ * "algorithm": "rsa-sha256",
+ * "headers": [
+ * "date" or "x-date",
+ * "content-md5"
+ * ],
+ * "signature": "base64"
+ * },
+ * "signingString": "ready to be passed to crypto.verify()"
+ * }
+ *
+ * @param {Object} request an http.ServerRequest.
+ * @param {Object} options an optional options object with:
+ * - clockSkew: allowed clock skew in seconds (default 300).
+ * - headers: required header names (def: date or x-date)
+ * - algorithms: algorithms to support (default: all).
+ * @return {Object} parsed out object (see above).
+ * @throws {TypeError} on invalid input.
+ * @throws {InvalidHeaderError} on an invalid Authorization header error.
+ * @throws {InvalidParamsError} if the params in the scheme are invalid.
+ * @throws {MissingHeaderError} if the params indicate a header not present,
+ * either in the request headers from the params,
+ * or not in the params from a required header
+ * in options.
+ * @throws {ExpiredRequestError} if the value of date or x-date exceeds skew.
+ */
+ parseRequest: function parseRequest(request, options) {
+ assert.object(request, 'request');
+ assert.object(request.headers, 'request.headers');
+ if (options === undefined) {
+ options = {};
+ }
+ if (options.headers === undefined) {
+ options.headers = [request.headers['x-date'] ? 'x-date' : 'date'];
+ }
+ assert.object(options, 'options');
+ assert.arrayOfString(options.headers, 'options.headers');
+ assert.optionalNumber(options.clockSkew, 'options.clockSkew');
+
+ if (!request.headers.authorization)
+ throw new MissingHeaderError('no authorization header present in ' +
+ 'the request');
+
+ options.clockSkew = options.clockSkew || 300;
+
+
+ var i = 0;
+ var state = State.New;
+ var substate = ParamsState.Name;
+ var tmpName = '';
+ var tmpValue = '';
+
+ var parsed = {
+ scheme: '',
+ params: {},
+ signingString: '',
+
+ get algorithm() {
+ return this.params.algorithm.toUpperCase();
+ },
+
+ get keyId() {
+ return this.params.keyId;
+ }
+
+ };
+
+ var authz = request.headers.authorization;
+ for (i = 0; i < authz.length; i++) {
+ var c = authz.charAt(i);
+
+ switch (Number(state)) {
+
+ case State.New:
+ if (c !== ' ') parsed.scheme += c;
+ else state = State.Params;
+ break;
+
+ case State.Params:
+ switch (Number(substate)) {
+
+ case ParamsState.Name:
+ var code = c.charCodeAt(0);
+ // restricted name of A-Z / a-z
+ if ((code >= 0x41 && code <= 0x5a) || // A-Z
+ (code >= 0x61 && code <= 0x7a)) { // a-z
+ tmpName += c;
+ } else if (c === '=') {
+ if (tmpName.length === 0)
+ throw new InvalidHeaderError('bad param format');
+ substate = ParamsState.Quote;
+ } else {
+ throw new InvalidHeaderError('bad param format');
+ }
+ break;
+
+ case ParamsState.Quote:
+ if (c === '"') {
+ tmpValue = '';
+ substate = ParamsState.Value;
+ } else {
+ throw new InvalidHeaderError('bad param format');
+ }
+ break;
+
+ case ParamsState.Value:
+ if (c === '"') {
+ parsed.params[tmpName] = tmpValue;
+ substate = ParamsState.Comma;
+ } else {
+ tmpValue += c;
+ }
+ break;
+
+ case ParamsState.Comma:
+ if (c === ',') {
+ tmpName = '';
+ substate = ParamsState.Name;
+ } else {
+ throw new InvalidHeaderError('bad param format');
+ }
+ break;
+
+ default:
+ throw new Error('Invalid substate');
+ }
+ break;
+
+ default:
+ throw new Error('Invalid substate');
+ }
+
+ }
+
+ if (!parsed.params.headers || parsed.params.headers === '') {
+ if (request.headers['x-date']) {
+ parsed.params.headers = ['x-date'];
+ } else {
+ parsed.params.headers = ['date'];
+ }
+ } else {
+ parsed.params.headers = parsed.params.headers.split(' ');
+ }
+
+ // Minimally validate the parsed object
+ if (!parsed.scheme || parsed.scheme !== 'Signature')
+ throw new InvalidHeaderError('scheme was not "Signature"');
+
+ if (!parsed.params.keyId)
+ throw new InvalidHeaderError('keyId was not specified');
+
+ if (!parsed.params.algorithm)
+ throw new InvalidHeaderError('algorithm was not specified');
+
+ if (!parsed.params.signature)
+ throw new InvalidHeaderError('signature was not specified');
+
+ // Check the algorithm against the official list
+ parsed.params.algorithm = parsed.params.algorithm.toLowerCase();
+ if (!Algorithms[parsed.params.algorithm])
+ throw new InvalidParamsError(parsed.params.algorithm +
+ ' is not supported');
+
+ // Build the signingString
+ for (i = 0; i < parsed.params.headers.length; i++) {
+ var h = parsed.params.headers[i].toLowerCase();
+ parsed.params.headers[i] = h;
+
+ if (h !== 'request-line') {
+ var value = request.headers[h];
+ if (!value)
+ throw new MissingHeaderError(h + ' was not in the request');
+ parsed.signingString += h + ': ' + value;
+ } else {
+ parsed.signingString +=
+ request.method + ' ' + request.url + ' HTTP/' + request.httpVersion;
+ }
+
+ if ((i + 1) < parsed.params.headers.length)
+ parsed.signingString += '\n';
+ }
+
+ // Check against the constraints
+ var date;
+ if (request.headers.date || request.headers['x-date']) {
+ if (request.headers['x-date']) {
+ date = new Date(request.headers['x-date']);
+ } else {
+ date = new Date(request.headers.date);
+ }
+ var now = new Date();
+ var skew = Math.abs(now.getTime() - date.getTime());
+
+ if (skew > options.clockSkew * 1000) {
+ throw new ExpiredRequestError('clock skew of ' +
+ (skew / 1000) +
+ 's was greater than ' +
+ options.clockSkew + 's');
+ }
+ }
+
+ options.headers.forEach(function (hdr) {
+ // Remember that we already checked any headers in the params
+ // were in the request, so if this passes we're good.
+ if (parsed.params.headers.indexOf(hdr) < 0)
+ throw new MissingHeaderError(hdr + ' was not a signed header');
+ });
+
+ if (options.algorithms) {
+ if (options.algorithms.indexOf(parsed.params.algorithm) === -1)
+ throw new InvalidParamsError(parsed.params.algorithm +
+ ' is not a supported algorithm');
+ }
+
+ return parsed;
+ }
+
+};
--- /dev/null
+// Copyright 2012 Joyent, Inc. All rights reserved.
+
+var assert = require('assert-plus');
+var crypto = require('crypto');
+var http = require('http');
+
+var sprintf = require('util').format;
+
+
+
+///--- Globals
+
+var Algorithms = {
+ 'rsa-sha1': true,
+ 'rsa-sha256': true,
+ 'rsa-sha512': true,
+ 'dsa-sha1': true,
+ 'hmac-sha1': true,
+ 'hmac-sha256': true,
+ 'hmac-sha512': true
+};
+
+var Authorization =
+ 'Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"';
+
+
+
+///--- Specific Errors
+
+function MissingHeaderError(message) {
+ this.name = 'MissingHeaderError';
+ this.message = message;
+ this.stack = (new Error()).stack;
+}
+MissingHeaderError.prototype = new Error();
+
+
+function InvalidAlgorithmError(message) {
+ this.name = 'InvalidAlgorithmError';
+ this.message = message;
+ this.stack = (new Error()).stack;
+}
+InvalidAlgorithmError.prototype = new Error();
+
+
+
+///--- Internal Functions
+
+function _pad(val) {
+ if (parseInt(val, 10) < 10) {
+ val = '0' + val;
+ }
+ return val;
+}
+
+
+function _rfc1123() {
+ var date = new Date();
+
+ var months = ['Jan',
+ 'Feb',
+ 'Mar',
+ 'Apr',
+ 'May',
+ 'Jun',
+ 'Jul',
+ 'Aug',
+ 'Sep',
+ 'Oct',
+ 'Nov',
+ 'Dec'];
+ var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+ return days[date.getUTCDay()] + ', ' +
+ _pad(date.getUTCDate()) + ' ' +
+ months[date.getUTCMonth()] + ' ' +
+ date.getUTCFullYear() + ' ' +
+ _pad(date.getUTCHours()) + ':' +
+ _pad(date.getUTCMinutes()) + ':' +
+ _pad(date.getUTCSeconds()) +
+ ' GMT';
+}
+
+
+
+///--- Exported API
+
+module.exports = {
+
+ /**
+ * Adds an 'Authorization' header to an http.ClientRequest object.
+ *
+ * Note that this API will add a Date header if it's not already set. Any
+ * other headers in the options.headers array MUST be present, or this
+ * will throw.
+ *
+ * You shouldn't need to check the return type; it's just there if you want
+ * to be pedantic.
+ *
+ * @param {Object} request an instance of http.ClientRequest.
+ * @param {Object} options signing parameters object:
+ * - {String} keyId required.
+ * - {String} key required (either a PEM or HMAC key).
+ * - {Array} headers optional; defaults to ['date'].
+ * - {String} algorithm optional; defaults to 'rsa-sha256'.
+ * - {String} httpVersion optional; defaults to '1.1'.
+ * @return {Boolean} true if Authorization (and optionally Date) were added.
+ * @throws {TypeError} on bad parameter types (input).
+ * @throws {InvalidAlgorithmError} if algorithm was bad.
+ * @throws {MissingHeaderError} if a header to be signed was specified but
+ * was not present.
+ */
+ signRequest: function signRequest(request, options) {
+ assert.object(request, 'request');
+ assert.object(options, 'options');
+ assert.optionalString(options.algorithm, 'options.algorithm');
+ assert.string(options.keyId, 'options.keyId');
+ assert.optionalArrayOfString(options.headers, 'options.headers');
+ assert.optionalString(options.httpVersion, 'options.httpVersion');
+
+ if (!request.getHeader('Date'))
+ request.setHeader('Date', _rfc1123());
+ if (!options.headers)
+ options.headers = ['date'];
+ if (!options.algorithm)
+ options.algorithm = 'rsa-sha256';
+ if (!options.httpVersion)
+ options.httpVersion = '1.1';
+
+ options.algorithm = options.algorithm.toLowerCase();
+
+ if (!Algorithms[options.algorithm])
+ throw new InvalidAlgorithmError(options.algorithm + ' is not supported');
+
+ var i;
+ var stringToSign = '';
+ for (i = 0; i < options.headers.length; i++) {
+ if (typeof (options.headers[i]) !== 'string')
+ throw new TypeError('options.headers must be an array of Strings');
+
+ var h = options.headers[i].toLowerCase();
+
+ if (h !== 'request-line') {
+ var value = request.getHeader(h);
+ if (!value) {
+ throw new MissingHeaderError(h + ' was not in the request');
+ }
+ stringToSign += h + ': ' + value;
+ } else {
+ stringToSign +=
+ request.method + ' ' + request.path + ' HTTP/' + options.httpVersion;
+ }
+
+ if ((i + 1) < options.headers.length)
+ stringToSign += '\n';
+ }
+
+ var alg = options.algorithm.match(/(hmac|rsa)-(\w+)/);
+ var signature;
+ if (alg[1] === 'hmac') {
+ var hmac = crypto.createHmac(alg[2].toUpperCase(), options.key);
+ hmac.update(stringToSign);
+ signature = hmac.digest('base64');
+ } else {
+ var signer = crypto.createSign(options.algorithm.toUpperCase());
+ signer.update(stringToSign);
+ signature = signer.sign(options.key, 'base64');
+ }
+
+ request.setHeader('Authorization', sprintf(Authorization,
+ options.keyId,
+ options.algorithm,
+ options.headers.join(' '),
+ signature));
+
+ return true;
+ }
+
+};
--- /dev/null
+// Copyright 2012 Joyent, Inc. All rights reserved.
+
+var assert = require('assert-plus');
+var crypto = require('crypto');
+
+var asn1 = require('asn1');
+var ctype = require('ctype');
+
+
+
+///--- Helpers
+
+function readNext(buffer, offset) {
+ var len = ctype.ruint32(buffer, 'big', offset);
+ offset += 4;
+
+ var newOffset = offset + len;
+
+ return {
+ data: buffer.slice(offset, newOffset),
+ offset: newOffset
+ };
+}
+
+
+function writeInt(writer, buffer) {
+ writer.writeByte(0x02); // ASN1.Integer
+ writer.writeLength(buffer.length);
+
+ for (var i = 0; i < buffer.length; i++)
+ writer.writeByte(buffer[i]);
+
+ return writer;
+}
+
+
+function rsaToPEM(key) {
+ var buffer;
+ var der;
+ var exponent;
+ var i;
+ var modulus;
+ var newKey = '';
+ var offset = 0;
+ var type;
+ var tmp;
+
+ try {
+ buffer = new Buffer(key.split(' ')[1], 'base64');
+
+ tmp = readNext(buffer, offset);
+ type = tmp.data.toString();
+ offset = tmp.offset;
+
+ if (type !== 'ssh-rsa')
+ throw new Error('Invalid ssh key type: ' + type);
+
+ tmp = readNext(buffer, offset);
+ exponent = tmp.data;
+ offset = tmp.offset;
+
+ tmp = readNext(buffer, offset);
+ modulus = tmp.data;
+ } catch (e) {
+ throw new Error('Invalid ssh key: ' + key);
+ }
+
+ // DER is a subset of BER
+ der = new asn1.BerWriter();
+
+ der.startSequence();
+
+ der.startSequence();
+ der.writeOID('1.2.840.113549.1.1.1');
+ der.writeNull();
+ der.endSequence();
+
+ der.startSequence(0x03); // bit string
+ der.writeByte(0x00);
+
+ // Actual key
+ der.startSequence();
+ writeInt(der, modulus);
+ writeInt(der, exponent);
+ der.endSequence();
+
+ // bit string
+ der.endSequence();
+
+ der.endSequence();
+
+ tmp = der.buffer.toString('base64');
+ for (i = 0; i < tmp.length; i++) {
+ if ((i % 64) === 0)
+ newKey += '\n';
+ newKey += tmp.charAt(i);
+ }
+
+ if (!/\\n$/.test(newKey))
+ newKey += '\n';
+
+ return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n';
+}
+
+
+function dsaToPEM(key) {
+ var buffer;
+ var offset = 0;
+ var tmp;
+ var der;
+ var newKey = '';
+
+ var type;
+ var p;
+ var q;
+ var g;
+ var y;
+
+ try {
+ buffer = new Buffer(key.split(' ')[1], 'base64');
+
+ tmp = readNext(buffer, offset);
+ type = tmp.data.toString();
+ offset = tmp.offset;
+
+ /* JSSTYLED */
+ if (!/^ssh-ds[as].*/.test(type))
+ throw new Error('Invalid ssh key type: ' + type);
+
+ tmp = readNext(buffer, offset);
+ p = tmp.data;
+ offset = tmp.offset;
+
+ tmp = readNext(buffer, offset);
+ q = tmp.data;
+ offset = tmp.offset;
+
+ tmp = readNext(buffer, offset);
+ g = tmp.data;
+ offset = tmp.offset;
+
+ tmp = readNext(buffer, offset);
+ y = tmp.data;
+ } catch (e) {
+ console.log(e.stack);
+ throw new Error('Invalid ssh key: ' + key);
+ }
+
+ // DER is a subset of BER
+ der = new asn1.BerWriter();
+
+ der.startSequence();
+
+ der.startSequence();
+ der.writeOID('1.2.840.10040.4.1');
+
+ der.startSequence();
+ writeInt(der, p);
+ writeInt(der, q);
+ writeInt(der, g);
+ der.endSequence();
+
+ der.endSequence();
+
+ der.startSequence(0x03); // bit string
+ der.writeByte(0x00);
+ writeInt(der, y);
+ der.endSequence();
+
+ der.endSequence();
+
+ tmp = der.buffer.toString('base64');
+ for (var i = 0; i < tmp.length; i++) {
+ if ((i % 64) === 0)
+ newKey += '\n';
+ newKey += tmp.charAt(i);
+ }
+
+ if (!/\\n$/.test(newKey))
+ newKey += '\n';
+
+ return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n';
+}
+
+
+///--- API
+
+module.exports = {
+
+ /**
+ * Converts an OpenSSH public key (rsa only) to a PKCS#8 PEM file.
+ *
+ * The intent of this module is to interoperate with OpenSSL only,
+ * specifically the node crypto module's `verify` method.
+ *
+ * @param {String} key an OpenSSH public key.
+ * @return {String} PEM encoded form of the RSA public key.
+ * @throws {TypeError} on bad input.
+ * @throws {Error} on invalid ssh key formatted data.
+ */
+ sshKeyToPEM: function sshKeyToPEM(key) {
+ assert.string(key, 'ssh_key');
+
+ /* JSSTYLED */
+ if (/^ssh-rsa.*/.test(key))
+ return rsaToPEM(key);
+
+ /* JSSTYLED */
+ if (/^ssh-ds[as].*/.test(key))
+ return dsaToPEM(key);
+
+ throw new Error('Only RSA and DSA public keys are allowed');
+ },
+
+
+ /**
+ * Generates an OpenSSH fingerprint from an ssh public key.
+ *
+ * @param {String} key an OpenSSH public key.
+ * @return {String} key fingerprint.
+ * @throws {TypeError} on bad input.
+ * @throws {Error} if what you passed doesn't look like an ssh public key.
+ */
+ fingerprint: function fingerprint(key) {
+ assert.string(key, 'ssh_key');
+
+ var pieces = key.split(' ');
+ if (!pieces || !pieces.length || pieces.length < 2)
+ throw new Error('invalid ssh key');
+
+ var data = new Buffer(pieces[1], 'base64');
+
+ var hash = crypto.createHash('md5');
+ hash.update(data);
+ var digest = hash.digest('hex');
+
+ var fp = '';
+ for (var i = 0; i < digest.length; i++) {
+ if (i && i % 2 === 0)
+ fp += ':';
+
+ fp += digest[i];
+ }
+
+ return fp;
+ },
+
+ /**
+ * Converts a PKGCS#8 PEM file to an OpenSSH public key (rsa)
+ *
+ * The reverse of the above function.
+ */
+ pemToRsaSSHKey: function pemToRsaSSHKey(pem, comment) {
+ assert.equal('string', typeof pem, 'typeof pem');
+
+ // chop off the BEGIN PUBLIC KEY and END PUBLIC KEY portion
+ var cleaned = pem.split('\n').slice(1, -2).join('');
+
+ var buf = new Buffer(cleaned, 'base64');
+
+ var der = new asn1.BerReader(buf);
+
+ der.readSequence();
+ der.readSequence();
+
+ var oid = der.readOID();
+ assert.equal(oid, '1.2.840.113549.1.1.1', 'pem not in RSA format');
+
+ // Null -- XXX this probably isn't good practice
+ der.readByte();
+ der.readByte();
+
+ // bit string sequence
+ der.readSequence(0x03);
+ der.readByte();
+ der.readSequence();
+
+ // modulus
+ assert.equal(der.peek(), asn1.Ber.Integer, 'modulus not an integer');
+ der._offset = der.readLength(der.offset + 1);
+ var modulus = der._buf.slice(der.offset, der.offset + der.length);
+ der._offset += der.length;
+
+ // exponent
+ assert.equal(der.peek(), asn1.Ber.Integer, 'exponent not an integer');
+ der._offset = der.readLength(der.offset + 1);
+ var exponent = der._buf.slice(der.offset, der.offset + der.length);
+ der._offset += der.length;
+
+ // now, make the key
+ var type = new Buffer('ssh-rsa');
+ var buffer = new Buffer(4 + type.length + 4 + modulus.length + 4 + exponent.length);
+ var i = 0;
+ buffer.writeUInt32BE(type.length, i); i += 4;
+ type.copy(buffer, i); i += type.length;
+ buffer.writeUInt32BE(exponent.length, i); i += 4;
+ exponent.copy(buffer, i); i += exponent.length;
+ buffer.writeUInt32BE(modulus.length, i); i += 4;
+ modulus.copy(buffer, i); i += modulus.length;
+
+ var s = type.toString() + ' ' + buffer.toString('base64') + ' ' + (comment || '');
+ return s;
+ }
+};
--- /dev/null
+// Copyright 2011 Joyent, Inc. All rights reserved.
+
+var assert = require('assert-plus');
+var crypto = require('crypto');
+
+
+
+///--- Exported API
+
+module.exports = {
+
+ /**
+ * Simply wraps up the node crypto operations for you, and returns
+ * true or false. You are expected to pass in an object that was
+ * returned from `parse()`.
+ *
+ * @param {Object} parsedSignature the object you got from `parse`.
+ * @param {String} key either an RSA private key PEM or HMAC secret.
+ * @return {Boolean} true if valid, false otherwise.
+ * @throws {TypeError} if you pass in bad arguments.
+ */
+ verifySignature: function verifySignature(parsedSignature, key) {
+ assert.object(parsedSignature, 'parsedSignature');
+ assert.string(key, 'key');
+
+ var alg = parsedSignature.algorithm.match(/(HMAC|RSA|DSA)-(\w+)/);
+ if (!alg || alg.length !== 3)
+ throw new TypeError('parsedSignature: unsupported algorithm ' +
+ parsedSignature.algorithm);
+
+ if (alg[1] === 'HMAC') {
+ var hmac = crypto.createHmac(alg[2].toUpperCase(), key);
+ hmac.update(parsedSignature.signingString);
+ return (hmac.digest('base64') === parsedSignature.params.signature);
+ } else {
+ var verify = crypto.createVerify(alg[0]);
+ verify.update(parsedSignature.signingString);
+ return verify.verify(key, parsedSignature.params.signature, 'base64');
+ }
+ }
+
+};
--- /dev/null
+node_modules
+*.log
--- /dev/null
+Copyright (c) 2011 Mark Cavage, 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
--- /dev/null
+node-asn1 is a library for encoding and decoding ASN.1 datatypes in pure JS.
+Currently BER encoding is supported; at some point I'll likely have to do DER.
+
+## Usage
+
+Mostly, if you're *actually* needing to read and write ASN.1, you probably don't
+need this readme to explain what and why. If you have no idea what ASN.1 is,
+see this: ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc
+
+The source is pretty much self-explanatory, and has read/write methods for the
+common types out there.
+
+### Decoding
+
+The following reads an ASN.1 sequence with a boolean.
+
+ var Ber = require('asn1').Ber;
+
+ var reader = new Ber.Reader(new Buffer([0x30, 0x03, 0x01, 0x01, 0xff]));
+
+ reader.readSequence();
+ console.log('Sequence len: ' + reader.length);
+ if (reader.peek() === Ber.Boolean)
+ console.log(reader.readBoolean());
+
+### Encoding
+
+The following generates the same payload as above.
+
+ var Ber = require('asn1').Ber;
+
+ var writer = new Ber.Writer();
+
+ writer.startSequence();
+ writer.writeBoolean(true);
+ writer.endSequence();
+
+ console.log(writer.buffer);
+
+## Installation
+
+ npm install asn1
+
+## License
+
+MIT.
+
+## Bugs
+
+See <https://github.com/mcavage/node-asn1/issues>.
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+
+module.exports = {
+
+ newInvalidAsn1Error: function(msg) {
+ var e = new Error();
+ e.name = 'InvalidAsn1Error';
+ e.message = msg || '';
+ return e;
+ }
+
+};
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var errors = require('./errors');
+var types = require('./types');
+
+var Reader = require('./reader');
+var Writer = require('./writer');
+
+
+///--- Exports
+
+module.exports = {
+
+ Reader: Reader,
+
+ Writer: Writer
+
+};
+
+for (var t in types) {
+ if (types.hasOwnProperty(t))
+ module.exports[t] = types[t];
+}
+for (var e in errors) {
+ if (errors.hasOwnProperty(e))
+ module.exports[e] = errors[e];
+}
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var assert = require('assert');
+
+var ASN1 = require('./types');
+var errors = require('./errors');
+
+
+///--- Globals
+
+var newInvalidAsn1Error = errors.newInvalidAsn1Error;
+
+
+
+///--- API
+
+function Reader(data) {
+ if (!data || !Buffer.isBuffer(data))
+ throw new TypeError('data must be a node Buffer');
+
+ this._buf = data;
+ this._size = data.length;
+
+ // These hold the "current" state
+ this._len = 0;
+ this._offset = 0;
+
+ var self = this;
+ this.__defineGetter__('length', function() { return self._len; });
+ this.__defineGetter__('offset', function() { return self._offset; });
+ this.__defineGetter__('remain', function() {
+ return self._size - self._offset;
+ });
+ this.__defineGetter__('buffer', function() {
+ return self._buf.slice(self._offset);
+ });
+}
+
+
+/**
+ * Reads a single byte and advances offset; you can pass in `true` to make this
+ * a "peek" operation (i.e., get the byte, but don't advance the offset).
+ *
+ * @param {Boolean} peek true means don't move offset.
+ * @return {Number} the next byte, null if not enough data.
+ */
+Reader.prototype.readByte = function(peek) {
+ if (this._size - this._offset < 1)
+ return null;
+
+ var b = this._buf[this._offset] & 0xff;
+
+ if (!peek)
+ this._offset += 1;
+
+ return b;
+};
+
+
+Reader.prototype.peek = function() {
+ return this.readByte(true);
+};
+
+
+/**
+ * Reads a (potentially) variable length off the BER buffer. This call is
+ * not really meant to be called directly, as callers have to manipulate
+ * the internal buffer afterwards.
+ *
+ * As a result of this call, you can call `Reader.length`, until the
+ * next thing called that does a readLength.
+ *
+ * @return {Number} the amount of offset to advance the buffer.
+ * @throws {InvalidAsn1Error} on bad ASN.1
+ */
+Reader.prototype.readLength = function(offset) {
+ if (offset === undefined)
+ offset = this._offset;
+
+ if (offset >= this._size)
+ return null;
+
+ var lenB = this._buf[offset++] & 0xff;
+ if (lenB === null)
+ return null;
+
+ if ((lenB & 0x80) == 0x80) {
+ lenB &= 0x7f;
+
+ if (lenB == 0)
+ throw newInvalidAsn1Error('Indefinite length not supported');
+
+ if (lenB > 4)
+ throw newInvalidAsn1Error('encoding too long');
+
+ if (this._size - offset < lenB)
+ return null;
+
+ this._len = 0;
+ for (var i = 0; i < lenB; i++)
+ this._len = (this._len << 8) + (this._buf[offset++] & 0xff);
+
+ } else {
+ // Wasn't a variable length
+ this._len = lenB;
+ }
+
+ return offset;
+};
+
+
+/**
+ * Parses the next sequence in this BER buffer.
+ *
+ * To get the length of the sequence, call `Reader.length`.
+ *
+ * @return {Number} the sequence's tag.
+ */
+Reader.prototype.readSequence = function(tag) {
+ var seq = this.peek();
+ if (seq === null)
+ return null;
+ if (tag !== undefined && tag !== seq)
+ throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+ ': got 0x' + seq.toString(16));
+
+ var o = this.readLength(this._offset + 1); // stored in `length`
+ if (o === null)
+ return null;
+
+ this._offset = o;
+ return seq;
+};
+
+
+Reader.prototype.readInt = function() {
+ return this._readTag(ASN1.Integer);
+};
+
+
+Reader.prototype.readBoolean = function() {
+ return (this._readTag(ASN1.Boolean) === 0 ? false : true);
+};
+
+
+Reader.prototype.readEnumeration = function() {
+ return this._readTag(ASN1.Enumeration);
+};
+
+
+Reader.prototype.readString = function(tag, retbuf) {
+ if (!tag)
+ tag = ASN1.OctetString;
+
+ var b = this.peek();
+ if (b === null)
+ return null;
+
+ if (b !== tag)
+ throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+ ': got 0x' + b.toString(16));
+
+ var o = this.readLength(this._offset + 1); // stored in `length`
+
+ if (o === null)
+ return null;
+
+ if (this.length > this._size - o)
+ return null;
+
+ this._offset = o;
+
+ if (this.length === 0)
+ return '';
+
+ var str = this._buf.slice(this._offset, this._offset + this.length);
+ this._offset += this.length;
+
+ return retbuf ? str : str.toString('utf8');
+};
+
+Reader.prototype.readOID = function(tag) {
+ if (!tag)
+ tag = ASN1.OID;
+
+ var b = this.peek();
+ if (b === null)
+ return null;
+
+ if (b !== tag)
+ throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+ ': got 0x' + b.toString(16));
+
+ var o = this.readLength(this._offset + 1); // stored in `length`
+ if (o === null)
+ return null;
+
+ if (this.length > this._size - o)
+ return null;
+
+ this._offset = o;
+
+ var values = [];
+ var value = 0;
+
+ for (var i = 0; i < this.length; i++) {
+ var byte = this._buf[this._offset++] & 0xff;
+
+ value <<= 7;
+ value += byte & 0x7f;
+ if ((byte & 0x80) == 0) {
+ values.push(value);
+ value = 0;
+ }
+ }
+
+ value = values.shift();
+ values.unshift(value % 40);
+ values.unshift((value / 40) >> 0);
+
+ return values.join('.');
+};
+
+
+Reader.prototype._readTag = function(tag) {
+ assert.ok(tag !== undefined);
+
+ var b = this.peek();
+
+ if (b === null)
+ return null;
+
+ if (b !== tag)
+ throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+ ': got 0x' + b.toString(16));
+
+ var o = this.readLength(this._offset + 1); // stored in `length`
+ if (o === null)
+ return null;
+
+ if (this.length > 4)
+ throw newInvalidAsn1Error('Integer too long: ' + this.length);
+
+ if (this.length > this._size - o)
+ return null;
+ this._offset = o;
+
+ var fb = this._buf[this._offset++];
+ var value = 0;
+
+ value = fb & 0x7F;
+ for (var i = 1; i < this.length; i++) {
+ value <<= 8;
+ value |= (this._buf[this._offset++] & 0xff);
+ }
+
+ if ((fb & 0x80) == 0x80)
+ value = -value;
+
+ return value;
+};
+
+
+
+///--- Exported API
+
+module.exports = Reader;
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+
+module.exports = {
+ EOC: 0,
+ Boolean: 1,
+ Integer: 2,
+ BitString: 3,
+ OctetString: 4,
+ Null: 5,
+ OID: 6,
+ ObjectDescriptor: 7,
+ External: 8,
+ Real: 9, // float
+ Enumeration: 10,
+ PDV: 11,
+ Utf8String: 12,
+ RelativeOID: 13,
+ Sequence: 16,
+ Set: 17,
+ NumericString: 18,
+ PrintableString: 19,
+ T61String: 20,
+ VideotexString: 21,
+ IA5String: 22,
+ UTCTime: 23,
+ GeneralizedTime: 24,
+ GraphicString: 25,
+ VisibleString: 26,
+ GeneralString: 28,
+ UniversalString: 29,
+ CharacterString: 30,
+ BMPString: 31,
+ Constructor: 32,
+ Context: 128
+};
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var assert = require('assert');
+var ASN1 = require('./types');
+var errors = require('./errors');
+
+
+///--- Globals
+
+var newInvalidAsn1Error = errors.newInvalidAsn1Error;
+
+var DEFAULT_OPTS = {
+ size: 1024,
+ growthFactor: 8
+};
+
+
+///--- Helpers
+
+function merge(from, to) {
+ assert.ok(from);
+ assert.equal(typeof(from), 'object');
+ assert.ok(to);
+ assert.equal(typeof(to), 'object');
+
+ var keys = Object.getOwnPropertyNames(from);
+ keys.forEach(function(key) {
+ if (to[key])
+ return;
+
+ var value = Object.getOwnPropertyDescriptor(from, key);
+ Object.defineProperty(to, key, value);
+ });
+
+ return to;
+}
+
+
+
+///--- API
+
+function Writer(options) {
+ options = merge(DEFAULT_OPTS, options || {});
+
+ this._buf = new Buffer(options.size || 1024);
+ this._size = this._buf.length;
+ this._offset = 0;
+ this._options = options;
+
+ // A list of offsets in the buffer where we need to insert
+ // sequence tag/len pairs.
+ this._seq = [];
+
+ var self = this;
+ this.__defineGetter__('buffer', function() {
+ if (self._seq.length)
+ throw new InvalidAsn1Error(self._seq.length + ' unended sequence(s)');
+
+ return self._buf.slice(0, self._offset);
+ });
+}
+
+
+Writer.prototype.writeByte = function(b) {
+ if (typeof(b) !== 'number')
+ throw new TypeError('argument must be a Number');
+
+ this._ensure(1);
+ this._buf[this._offset++] = b;
+};
+
+
+Writer.prototype.writeInt = function(i, tag) {
+ if (typeof(i) !== 'number')
+ throw new TypeError('argument must be a Number');
+ if (typeof(tag) !== 'number')
+ tag = ASN1.Integer;
+
+ var sz = 4;
+
+ while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) &&
+ (sz > 1)) {
+ sz--;
+ i <<= 8;
+ }
+
+ if (sz > 4)
+ throw new InvalidAsn1Error('BER ints cannot be > 0xffffffff');
+
+ this._ensure(2 + sz);
+ this._buf[this._offset++] = tag;
+ this._buf[this._offset++] = sz;
+
+ while (sz-- > 0) {
+ this._buf[this._offset++] = ((i & 0xff000000) >> 24);
+ i <<= 8;
+ }
+
+};
+
+
+Writer.prototype.writeNull = function() {
+ this.writeByte(ASN1.Null);
+ this.writeByte(0x00);
+};
+
+
+Writer.prototype.writeEnumeration = function(i, tag) {
+ if (typeof(i) !== 'number')
+ throw new TypeError('argument must be a Number');
+ if (typeof(tag) !== 'number')
+ tag = ASN1.Enumeration;
+
+ return this.writeInt(i, tag);
+};
+
+
+Writer.prototype.writeBoolean = function(b, tag) {
+ if (typeof(b) !== 'boolean')
+ throw new TypeError('argument must be a Boolean');
+ if (typeof(tag) !== 'number')
+ tag = ASN1.Boolean;
+
+ this._ensure(3);
+ this._buf[this._offset++] = tag;
+ this._buf[this._offset++] = 0x01;
+ this._buf[this._offset++] = b ? 0xff : 0x00;
+};
+
+
+Writer.prototype.writeString = function(s, tag) {
+ if (typeof(s) !== 'string')
+ throw new TypeError('argument must be a string (was: ' + typeof(s) + ')');
+ if (typeof(tag) !== 'number')
+ tag = ASN1.OctetString;
+
+ var len = Buffer.byteLength(s);
+ this.writeByte(tag);
+ this.writeLength(len);
+ if (len) {
+ this._ensure(len);
+ this._buf.write(s, this._offset);
+ this._offset += len;
+ }
+};
+
+
+Writer.prototype.writeBuffer = function(buf, tag) {
+ if (typeof(tag) !== 'number')
+ throw new TypeError('tag must be a number');
+ if (!Buffer.isBuffer(buf))
+ throw new TypeError('argument must be a buffer');
+
+ this.writeByte(tag);
+ this.writeLength(buf.length);
+ this._ensure(buf.length);
+ buf.copy(this._buf, this._offset, 0, buf.length);
+ this._offset += buf.length;
+};
+
+
+Writer.prototype.writeStringArray = function(strings) {
+ if ((!strings instanceof Array))
+ throw new TypeError('argument must be an Array[String]');
+
+ var self = this;
+ strings.forEach(function(s) {
+ self.writeString(s);
+ });
+};
+
+// This is really to solve DER cases, but whatever for now
+Writer.prototype.writeOID = function(s, tag) {
+ if (typeof(s) !== 'string')
+ throw new TypeError('argument must be a string');
+ if (typeof(tag) !== 'number')
+ tag = ASN1.OID;
+
+ if (!/^([0-9]+\.){3,}[0-9]+$/.test(s))
+ throw new Error('argument is not a valid OID string');
+
+ function encodeOctet(bytes, octet) {
+ if (octet < 128) {
+ bytes.push(octet);
+ } else if (octet < 16384) {
+ bytes.push((octet >>> 7) | 0x80);
+ bytes.push(octet & 0x7F);
+ } else if (octet < 2097152) {
+ bytes.push((octet >>> 14) | 0x80);
+ bytes.push(((octet >>> 7) | 0x80) & 0xFF);
+ bytes.push(octet & 0x7F);
+ } else if (octet < 268435456) {
+ bytes.push((octet >>> 21) | 0x80);
+ bytes.push(((octet >>> 14) | 0x80) & 0xFF);
+ bytes.push(((octet >>> 7) | 0x80) & 0xFF);
+ bytes.push(octet & 0x7F);
+ } else {
+ bytes.push(((octet >>> 28) | 0x80) & 0xFF);
+ bytes.push(((octet >>> 21) | 0x80) & 0xFF);
+ bytes.push(((octet >>> 14) | 0x80) & 0xFF);
+ bytes.push(((octet >>> 7) | 0x80) & 0xFF);
+ bytes.push(octet & 0x7F);
+ }
+ }
+
+ var tmp = s.split('.');
+ var bytes = [];
+ bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10));
+ tmp.slice(2).forEach(function(b) {
+ encodeOctet(bytes, parseInt(b, 10));
+ });
+
+ var self = this;
+ this._ensure(2 + bytes.length);
+ this.writeByte(tag);
+ this.writeLength(bytes.length);
+ bytes.forEach(function(b) {
+ self.writeByte(b);
+ });
+};
+
+
+Writer.prototype.writeLength = function(len) {
+ if (typeof(len) !== 'number')
+ throw new TypeError('argument must be a Number');
+
+ this._ensure(4);
+
+ if (len <= 0x7f) {
+ this._buf[this._offset++] = len;
+ } else if (len <= 0xff) {
+ this._buf[this._offset++] = 0x81;
+ this._buf[this._offset++] = len;
+ } else if (len <= 0xffff) {
+ this._buf[this._offset++] = 0x82;
+ this._buf[this._offset++] = len >> 8;
+ this._buf[this._offset++] = len;
+ } else if (len <= 0xffffff) {
+ this._shift(start, len, 1);
+ this._buf[this._offset++] = 0x83;
+ this._buf[this._offset++] = len >> 16;
+ this._buf[this._offset++] = len >> 8;
+ this._buf[this._offset++] = len;
+ } else {
+ throw new InvalidAsn1ERror('Length too long (> 4 bytes)');
+ }
+};
+
+Writer.prototype.startSequence = function(tag) {
+ if (typeof(tag) !== 'number')
+ tag = ASN1.Sequence | ASN1.Constructor;
+
+ this.writeByte(tag);
+ this._seq.push(this._offset);
+ this._ensure(3);
+ this._offset += 3;
+};
+
+
+Writer.prototype.endSequence = function() {
+ var seq = this._seq.pop();
+ var start = seq + 3;
+ var len = this._offset - start;
+
+ if (len <= 0x7f) {
+ this._shift(start, len, -2);
+ this._buf[seq] = len;
+ } else if (len <= 0xff) {
+ this._shift(start, len, -1);
+ this._buf[seq] = 0x81;
+ this._buf[seq + 1] = len;
+ } else if (len <= 0xffff) {
+ this._buf[seq] = 0x82;
+ this._buf[seq + 1] = len >> 8;
+ this._buf[seq + 2] = len;
+ } else if (len <= 0xffffff) {
+ this._shift(start, len, 1);
+ this._buf[seq] = 0x83;
+ this._buf[seq + 1] = len >> 16;
+ this._buf[seq + 2] = len >> 8;
+ this._buf[seq + 3] = len;
+ } else {
+ throw new InvalidAsn1Error('Sequence too long');
+ }
+};
+
+
+Writer.prototype._shift = function(start, len, shift) {
+ assert.ok(start !== undefined);
+ assert.ok(len !== undefined);
+ assert.ok(shift);
+
+ this._buf.copy(this._buf, start + shift, start, start + len);
+ this._offset += shift;
+};
+
+Writer.prototype._ensure = function(len) {
+ assert.ok(len);
+
+ if (this._size - this._offset < len) {
+ var sz = this._size * this._options.growthFactor;
+ if (sz - this._offset < len)
+ sz += len;
+
+ var buf = new Buffer(sz);
+
+ this._buf.copy(buf, 0, 0, this._offset);
+ this._buf = buf;
+ this._size = sz;
+ }
+};
+
+
+
+///--- Exported API
+
+module.exports = Writer;
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+// If you have no idea what ASN.1 or BER is, see this:
+// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc
+
+var Ber = require('./ber/index');
+
+
+
+///--- Exported API
+
+module.exports = {
+
+ Ber: Ber,
+
+ BerReader: Ber.Reader,
+
+ BerWriter: Ber.Writer
+
+};
--- /dev/null
+{
+ "author": {
+ "name": "Mark Cavage",
+ "email": "mcavage@gmail.com"
+ },
+ "contributors": [
+ {
+ "name": "David Gwynne",
+ "email": "loki@animata.net"
+ },
+ {
+ "name": "Yunong Xiao",
+ "email": "yunong@joyent.com"
+ }
+ ],
+ "name": "asn1",
+ "description": "Contains parsers and serializers for ASN.1 (currently BER only)",
+ "version": "0.1.11",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mcavage/node-asn1.git"
+ },
+ "main": "lib/index.js",
+ "engines": {
+ "node": ">=0.4.9"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "0.1.4"
+ },
+ "scripts": {
+ "pretest": "which gjslint; if [[ \"$?\" = 0 ]] ; then gjslint --nojsdoc -r lib -r tst; else echo \"Missing gjslint. Skipping lint\"; fi",
+ "test": "./node_modules/.bin/tap ./tst"
+ },
+ "_npmUser": {
+ "name": "mcavage",
+ "email": "mcavage@gmail.com"
+ },
+ "_id": "asn1@0.1.11",
+ "_engineSupported": true,
+ "_npmVersion": "1.1.0-beta-4",
+ "_nodeVersion": "v0.6.6",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "559be18376d08a4ec4dbe80877d27818639b2df7",
+ "tarball": "http://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "mcavage",
+ "email": "mcavage@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "559be18376d08a4ec4dbe80877d27818639b2df7",
+ "_resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz",
+ "_from": "asn1@0.1.11"
+}
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var test = require('tap').test;
+
+
+
+///--- Globals
+
+var BerReader;
+
+
+
+///--- Tests
+
+test('load library', function(t) {
+ BerReader = require('../../lib/index').BerReader;
+ t.ok(BerReader);
+ try {
+ new BerReader();
+ t.fail('Should have thrown');
+ } catch (e) {
+ t.ok(e instanceof TypeError, 'Should have been a type error');
+ }
+ t.end();
+});
+
+
+test('read byte', function(t) {
+ var reader = new BerReader(new Buffer([0xde]));
+ t.ok(reader);
+ t.equal(reader.readByte(), 0xde, 'wrong value');
+ t.end();
+});
+
+
+test('read 1 byte int', function(t) {
+ var reader = new BerReader(new Buffer([0x02, 0x01, 0x03]));
+ t.ok(reader);
+ t.equal(reader.readInt(), 0x03, 'wrong value');
+ t.equal(reader.length, 0x01, 'wrong length');
+ t.end();
+});
+
+
+test('read 2 byte int', function(t) {
+ var reader = new BerReader(new Buffer([0x02, 0x02, 0x7e, 0xde]));
+ t.ok(reader);
+ t.equal(reader.readInt(), 0x7ede, 'wrong value');
+ t.equal(reader.length, 0x02, 'wrong length');
+ t.end();
+});
+
+
+test('read 3 byte int', function(t) {
+ var reader = new BerReader(new Buffer([0x02, 0x03, 0x7e, 0xde, 0x03]));
+ t.ok(reader);
+ t.equal(reader.readInt(), 0x7ede03, 'wrong value');
+ t.equal(reader.length, 0x03, 'wrong length');
+ t.end();
+});
+
+
+test('read 4 byte int', function(t) {
+ var reader = new BerReader(new Buffer([0x02, 0x04, 0x7e, 0xde, 0x03, 0x01]));
+ t.ok(reader);
+ t.equal(reader.readInt(), 0x7ede0301, 'wrong value');
+ t.equal(reader.length, 0x04, 'wrong length');
+ t.end();
+});
+
+
+test('read boolean true', function(t) {
+ var reader = new BerReader(new Buffer([0x01, 0x01, 0xff]));
+ t.ok(reader);
+ t.equal(reader.readBoolean(), true, 'wrong value');
+ t.equal(reader.length, 0x01, 'wrong length');
+ t.end();
+});
+
+
+test('read boolean false', function(t) {
+ var reader = new BerReader(new Buffer([0x01, 0x01, 0x00]));
+ t.ok(reader);
+ t.equal(reader.readBoolean(), false, 'wrong value');
+ t.equal(reader.length, 0x01, 'wrong length');
+ t.end();
+});
+
+
+test('read enumeration', function(t) {
+ var reader = new BerReader(new Buffer([0x0a, 0x01, 0x20]));
+ t.ok(reader);
+ t.equal(reader.readEnumeration(), 0x20, 'wrong value');
+ t.equal(reader.length, 0x01, 'wrong length');
+ t.end();
+});
+
+
+test('read string', function(t) {
+ var dn = 'cn=foo,ou=unit,o=test';
+ var buf = new Buffer(dn.length + 2);
+ buf[0] = 0x04;
+ buf[1] = Buffer.byteLength(dn);
+ buf.write(dn, 2);
+ var reader = new BerReader(buf);
+ t.ok(reader);
+ t.equal(reader.readString(), dn, 'wrong value');
+ t.equal(reader.length, dn.length, 'wrong length');
+ t.end();
+});
+
+
+test('read sequence', function(t) {
+ var reader = new BerReader(new Buffer([0x30, 0x03, 0x01, 0x01, 0xff]));
+ t.ok(reader);
+ t.equal(reader.readSequence(), 0x30, 'wrong value');
+ t.equal(reader.length, 0x03, 'wrong length');
+ t.equal(reader.readBoolean(), true, 'wrong value');
+ t.equal(reader.length, 0x01, 'wrong length');
+ t.end();
+});
+
+
+test('anonymous LDAPv3 bind', function(t) {
+ var BIND = new Buffer(14);
+ BIND[0] = 0x30; // Sequence
+ BIND[1] = 12; // len
+ BIND[2] = 0x02; // ASN.1 Integer
+ BIND[3] = 1; // len
+ BIND[4] = 0x04; // msgid (make up 4)
+ BIND[5] = 0x60; // Bind Request
+ BIND[6] = 7; // len
+ BIND[7] = 0x02; // ASN.1 Integer
+ BIND[8] = 1; // len
+ BIND[9] = 0x03; // v3
+ BIND[10] = 0x04; // String (bind dn)
+ BIND[11] = 0; // len
+ BIND[12] = 0x80; // ContextSpecific (choice)
+ BIND[13] = 0; // simple bind
+
+ // Start testing ^^
+ var ber = new BerReader(BIND);
+ t.equal(ber.readSequence(), 48, 'Not an ASN.1 Sequence');
+ t.equal(ber.length, 12, 'Message length should be 12');
+ t.equal(ber.readInt(), 4, 'Message id should have been 4');
+ t.equal(ber.readSequence(), 96, 'Bind Request should have been 96');
+ t.equal(ber.length, 7, 'Bind length should have been 7');
+ t.equal(ber.readInt(), 3, 'LDAP version should have been 3');
+ t.equal(ber.readString(), '', 'Bind DN should have been empty');
+ t.equal(ber.length, 0, 'string length should have been 0');
+ t.equal(ber.readByte(), 0x80, 'Should have been ContextSpecific (choice)');
+ t.equal(ber.readByte(), 0, 'Should have been simple bind');
+ t.equal(null, ber.readByte(), 'Should be out of data');
+ t.end();
+});
+
+
+test('long string', function(t) {
+ var buf = new Buffer(256);
+ var o;
+ var s =
+ '2;649;CN=Red Hat CS 71GA Demo,O=Red Hat CS 71GA Demo,C=US;' +
+ 'CN=RHCS Agent - admin01,UID=admin01,O=redhat,C=US [1] This is ' +
+ 'Teena Vradmin\'s description.';
+ buf[0] = 0x04;
+ buf[1] = 0x81;
+ buf[2] = 0x94;
+ buf.write(s, 3);
+ var ber = new BerReader(buf.slice(0, 3 + s.length));
+ t.equal(ber.readString(), s);
+ t.end();
+});
--- /dev/null
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var test = require('tap').test;
+var sys = require('sys');
+
+///--- Globals
+
+var BerWriter;
+
+var BerReader;
+
+
+///--- Tests
+
+test('load library', function(t) {
+ BerWriter = require('../../lib/index').BerWriter;
+ t.ok(BerWriter);
+ t.ok(new BerWriter());
+ t.end();
+});
+
+
+test('write byte', function(t) {
+ var writer = new BerWriter();
+
+ writer.writeByte(0xC2);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 1, 'Wrong length');
+ t.equal(ber[0], 0xC2, 'value wrong');
+
+ t.end();
+});
+
+
+test('write 1 byte int', function(t) {
+ var writer = new BerWriter();
+
+ writer.writeInt(0x7f);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 3, 'Wrong length for an int: ' + ber.length);
+ t.equal(ber[0], 0x02, 'ASN.1 tag wrong (2) -> ' + ber[0]);
+ t.equal(ber[1], 0x01, 'length wrong(1) -> ' + ber[1]);
+ t.equal(ber[2], 0x7f, 'value wrong(3) -> ' + ber[2]);
+
+ t.end();
+});
+
+
+test('write 2 byte int', function(t) {
+ var writer = new BerWriter();
+
+ writer.writeInt(0x7ffe);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 4, 'Wrong length for an int');
+ t.equal(ber[0], 0x02, 'ASN.1 tag wrong');
+ t.equal(ber[1], 0x02, 'length wrong');
+ t.equal(ber[2], 0x7f, 'value wrong (byte 1)');
+ t.equal(ber[3], 0xfe, 'value wrong (byte 2)');
+
+ t.end();
+});
+
+
+test('write 3 byte int', function(t) {
+ var writer = new BerWriter();
+
+ writer.writeInt(0x7ffffe);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 5, 'Wrong length for an int');
+ t.equal(ber[0], 0x02, 'ASN.1 tag wrong');
+ t.equal(ber[1], 0x03, 'length wrong');
+ t.equal(ber[2], 0x7f, 'value wrong (byte 1)');
+ t.equal(ber[3], 0xff, 'value wrong (byte 2)');
+ t.equal(ber[4], 0xfe, 'value wrong (byte 3)');
+
+ t.end();
+});
+
+
+test('write 4 byte int', function(t) {
+ var writer = new BerWriter();
+
+ writer.writeInt(0x7ffffffe);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+
+ t.equal(ber.length, 6, 'Wrong length for an int');
+ t.equal(ber[0], 0x02, 'ASN.1 tag wrong');
+ t.equal(ber[1], 0x04, 'length wrong');
+ t.equal(ber[2], 0x7f, 'value wrong (byte 1)');
+ t.equal(ber[3], 0xff, 'value wrong (byte 2)');
+ t.equal(ber[4], 0xff, 'value wrong (byte 3)');
+ t.equal(ber[5], 0xfe, 'value wrong (byte 4)');
+
+ t.end();
+});
+
+
+test('write boolean', function(t) {
+ var writer = new BerWriter();
+
+ writer.writeBoolean(true);
+ writer.writeBoolean(false);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 6, 'Wrong length');
+ t.equal(ber[0], 0x01, 'tag wrong');
+ t.equal(ber[1], 0x01, 'length wrong');
+ t.equal(ber[2], 0xff, 'value wrong');
+ t.equal(ber[3], 0x01, 'tag wrong');
+ t.equal(ber[4], 0x01, 'length wrong');
+ t.equal(ber[5], 0x00, 'value wrong');
+
+ t.end();
+});
+
+
+test('write string', function(t) {
+ var writer = new BerWriter();
+ writer.writeString('hello world');
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 13, 'wrong length');
+ t.equal(ber[0], 0x04, 'wrong tag');
+ t.equal(ber[1], 11, 'wrong length');
+ t.equal(ber.slice(2).toString('utf8'), 'hello world', 'wrong value');
+
+ t.end();
+});
+
+test('write buffer', function(t) {
+ var writer = new BerWriter();
+ // write some stuff to start with
+ writer.writeString('hello world');
+ var ber = writer.buffer;
+ var buf = new Buffer([0x04, 0x0b, 0x30, 0x09, 0x02, 0x01, 0x0f, 0x01, 0x01,
+ 0xff, 0x01, 0x01, 0xff]);
+ writer.writeBuffer(buf.slice(2, buf.length), 0x04);
+ ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 26, 'wrong length');
+ t.equal(ber[0], 0x04, 'wrong tag');
+ t.equal(ber[1], 11, 'wrong length');
+ t.equal(ber.slice(2, 13).toString('utf8'), 'hello world', 'wrong value');
+ t.equal(ber[13], buf[0], 'wrong tag');
+ t.equal(ber[14], buf[1], 'wrong length');
+ for (var i = 13, j = 0; i < ber.length && j < buf.length; i++, j++) {
+ t.equal(ber[i], buf[j], 'buffer contents not identical');
+ }
+ t.end();
+});
+
+test('write string array', function(t) {
+ var writer = new BerWriter();
+ writer.writeStringArray(['hello world', 'fubar!']);
+ var ber = writer.buffer;
+
+ t.ok(ber);
+
+ t.equal(ber.length, 21, 'wrong length');
+ t.equal(ber[0], 0x04, 'wrong tag');
+ t.equal(ber[1], 11, 'wrong length');
+ t.equal(ber.slice(2, 13).toString('utf8'), 'hello world', 'wrong value');
+
+ t.equal(ber[13], 0x04, 'wrong tag');
+ t.equal(ber[14], 6, 'wrong length');
+ t.equal(ber.slice(15).toString('utf8'), 'fubar!', 'wrong value');
+
+ t.end();
+});
+
+
+test('resize internal buffer', function(t) {
+ var writer = new BerWriter({size: 2});
+ writer.writeString('hello world');
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 13, 'wrong length');
+ t.equal(ber[0], 0x04, 'wrong tag');
+ t.equal(ber[1], 11, 'wrong length');
+ t.equal(ber.slice(2).toString('utf8'), 'hello world', 'wrong value');
+
+ t.end();
+});
+
+
+test('sequence', function(t) {
+ var writer = new BerWriter({size: 25});
+ writer.startSequence();
+ writer.writeString('hello world');
+ writer.endSequence();
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ console.log(ber);
+ t.equal(ber.length, 15, 'wrong length');
+ t.equal(ber[0], 0x30, 'wrong tag');
+ t.equal(ber[1], 13, 'wrong length');
+ t.equal(ber[2], 0x04, 'wrong tag');
+ t.equal(ber[3], 11, 'wrong length');
+ t.equal(ber.slice(4).toString('utf8'), 'hello world', 'wrong value');
+
+ t.end();
+});
+
+
+test('nested sequence', function(t) {
+ var writer = new BerWriter({size: 25});
+ writer.startSequence();
+ writer.writeString('hello world');
+ writer.startSequence();
+ writer.writeString('hello world');
+ writer.endSequence();
+ writer.endSequence();
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 30, 'wrong length');
+ t.equal(ber[0], 0x30, 'wrong tag');
+ t.equal(ber[1], 28, 'wrong length');
+ t.equal(ber[2], 0x04, 'wrong tag');
+ t.equal(ber[3], 11, 'wrong length');
+ t.equal(ber.slice(4, 15).toString('utf8'), 'hello world', 'wrong value');
+ t.equal(ber[15], 0x30, 'wrong tag');
+ t.equal(ber[16], 13, 'wrong length');
+ t.equal(ber[17], 0x04, 'wrong tag');
+ t.equal(ber[18], 11, 'wrong length');
+ t.equal(ber.slice(19, 30).toString('utf8'), 'hello world', 'wrong value');
+
+ t.end();
+});
+
+
+test('LDAP bind message', function(t) {
+ var dn = 'cn=foo,ou=unit,o=test';
+ var writer = new BerWriter();
+ writer.startSequence();
+ writer.writeInt(3); // msgid = 3
+ writer.startSequence(0x60); // ldap bind
+ writer.writeInt(3); // ldap v3
+ writer.writeString(dn);
+ writer.writeByte(0x80);
+ writer.writeByte(0x00);
+ writer.endSequence();
+ writer.endSequence();
+ var ber = writer.buffer;
+
+ t.ok(ber);
+ t.equal(ber.length, 35, 'wrong length (buffer)');
+ t.equal(ber[0], 0x30, 'wrong tag');
+ t.equal(ber[1], 33, 'wrong length');
+ t.equal(ber[2], 0x02, 'wrong tag');
+ t.equal(ber[3], 1, 'wrong length');
+ t.equal(ber[4], 0x03, 'wrong value');
+ t.equal(ber[5], 0x60, 'wrong tag');
+ t.equal(ber[6], 28, 'wrong length');
+ t.equal(ber[7], 0x02, 'wrong tag');
+ t.equal(ber[8], 1, 'wrong length');
+ t.equal(ber[9], 0x03, 'wrong value');
+ t.equal(ber[10], 0x04, 'wrong tag');
+ t.equal(ber[11], dn.length, 'wrong length');
+ t.equal(ber.slice(12, 33).toString('utf8'), dn, 'wrong value');
+ t.equal(ber[33], 0x80, 'wrong tag');
+ t.equal(ber[34], 0x00, 'wrong len');
+
+ t.end();
+});
+
+
+test('Write OID', function(t) {
+ var oid = '1.2.840.113549.1.1.1';
+ var writer = new BerWriter();
+ writer.writeOID(oid);
+
+ var ber = writer.buffer;
+ t.ok(ber);
+ console.log(require('util').inspect(ber));
+ console.log(require('util').inspect(new Buffer([0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01])));
+
+ t.end();
+});
--- /dev/null
+# node-assert-plus
+
+This library is a super small wrapper over node's assert module that has two
+things: (1) the ability to disable assertions with the environment variable
+NODE_NDEBUG, and (2) some API wrappers for argument testing. Like
+`assert.string(myArg, 'myArg')`. As a simple example, most of my code looks
+like this:
+
+ var assert = require('assert-plus');
+
+ function fooAccount(options, callback) {
+ assert.object(options, 'options');
+ assert.number(options.id, 'options.id);
+ assert.bool(options.isManager, 'options.isManager');
+ assert.string(options.name, 'options.name');
+ assert.arrayOfString(options.email, 'options.email');
+ assert.func(callback, 'callback');
+
+ // Do stuff
+ callback(null, {});
+ }
+
+# API
+
+All methods that *aren't* part of node's core assert API are simply assumed to
+take an argument, and then a string 'name' that's not a message; `AssertionError`
+will be thrown if the assertion fails with a message like:
+
+ AssertionError: foo (string) is required
+ at test (/home/mark/work/foo/foo.js:3:9)
+ at Object.<anonymous> (/home/mark/work/foo/foo.js:15:1)
+ at Module._compile (module.js:446:26)
+ at Object..js (module.js:464:10)
+ at Module.load (module.js:353:31)
+ at Function._load (module.js:311:12)
+ at Array.0 (module.js:484:10)
+ at EventEmitter._tickCallback (node.js:190:38)
+
+from:
+
+ function test(foo) {
+ assert.string(foo, 'foo');
+ }
+
+There you go. You can check that arrays are of a homogenous type with `Arrayof$Type`:
+
+ function test(foo) {
+ assert.arrayOfString(foo, 'foo');
+ }
+
+You can assert IFF an argument is not `undefined` (i.e., an optional arg):
+
+ assert.optionalString(foo, 'foo');
+
+Lastly, you can opt-out of assertion checking altogether by setting the
+environment variable `NODE_NDEBUG=1`. This is pseudo-useful if you have
+lots of assertions, and don't want to pay `typeof ()` taxes to v8 in
+production.
+
+The complete list of APIs is:
+
+* assert.bool
+* assert.buffer
+* assert.func
+* assert.number
+* assert.object
+* assert.string
+* assert.arrayOfBool
+* assert.arrayOfFunc
+* assert.arrayOfNumber
+* assert.arrayOfObject
+* assert.arrayOfString
+* assert.optionalBool
+* assert.optionalBuffer
+* assert.optionalFunc
+* assert.optionalNumber
+* assert.optionalObject
+* assert.optionalString
+* assert.optionalArrayOfBool
+* assert.optionalArrayOfFunc
+* assert.optionalArrayOfNumber
+* assert.optionalArrayOfObject
+* assert.optionalArrayOfString
+* assert.AssertionError
+* assert.fail
+* assert.ok
+* assert.equal
+* assert.notEqual
+* assert.deepEqual
+* assert.notDeepEqual
+* assert.strictEqual
+* assert.notStrictEqual
+* assert.throws
+* assert.doesNotThrow
+* assert.ifError
+
+# Installation
+
+ npm install assert-plus
+
+## License
+
+The MIT License (MIT)
+Copyright (c) 2012 Mark Cavage
+
+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.
+
+## Bugs
+
+See <https://github.com/mcavage/node-assert-plus/issues>.
--- /dev/null
+// Copyright (c) 2012, Mark Cavage. All rights reserved.
+
+var assert = require('assert');
+var Stream = require('stream').Stream;
+var util = require('util');
+
+
+
+///--- Globals
+
+var NDEBUG = process.env.NODE_NDEBUG || false;
+var UUID_REGEXP = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
+
+
+
+///--- Messages
+
+var ARRAY_TYPE_REQUIRED = '%s ([%s]) required';
+var TYPE_REQUIRED = '%s (%s) is required';
+
+
+
+///--- Internal
+
+function capitalize(str) {
+ return (str.charAt(0).toUpperCase() + str.slice(1));
+}
+
+function uncapitalize(str) {
+ return (str.charAt(0).toLowerCase() + str.slice(1));
+}
+
+function _() {
+ return (util.format.apply(util, arguments));
+}
+
+
+function _assert(arg, type, name, stackFunc) {
+ if (!NDEBUG) {
+ name = name || type;
+ stackFunc = stackFunc || _assert.caller;
+ var t = typeof (arg);
+
+ if (t !== type) {
+ throw new assert.AssertionError({
+ message: _(TYPE_REQUIRED, name, type),
+ actual: t,
+ expected: type,
+ operator: '===',
+ stackStartFunction: stackFunc
+ });
+ }
+ }
+}
+
+
+function _instanceof(arg, type, name, stackFunc) {
+ if (!NDEBUG) {
+ name = name || type;
+ stackFunc = stackFunc || _instanceof.caller;
+
+ if (!(arg instanceof type)) {
+ throw new assert.AssertionError({
+ message: _(TYPE_REQUIRED, name, type.name),
+ actual: _getClass(arg),
+ expected: type.name,
+ operator: 'instanceof',
+ stackStartFunction: stackFunc
+ });
+ }
+ }
+}
+
+function _getClass(object) {
+ return (Object.prototype.toString.call(object).slice(8, -1));
+};
+
+
+
+///--- API
+
+function array(arr, type, name) {
+ if (!NDEBUG) {
+ name = name || type;
+
+ if (!Array.isArray(arr)) {
+ throw new assert.AssertionError({
+ message: _(ARRAY_TYPE_REQUIRED, name, type),
+ actual: typeof (arr),
+ expected: 'array',
+ operator: 'Array.isArray',
+ stackStartFunction: array.caller
+ });
+ }
+
+ for (var i = 0; i < arr.length; i++) {
+ _assert(arr[i], type, name, array);
+ }
+ }
+}
+
+
+function bool(arg, name) {
+ _assert(arg, 'boolean', name, bool);
+}
+
+
+function buffer(arg, name) {
+ if (!Buffer.isBuffer(arg)) {
+ throw new assert.AssertionError({
+ message: _(TYPE_REQUIRED, name || '', 'Buffer'),
+ actual: typeof (arg),
+ expected: 'buffer',
+ operator: 'Buffer.isBuffer',
+ stackStartFunction: buffer
+ });
+ }
+}
+
+
+function func(arg, name) {
+ _assert(arg, 'function', name);
+}
+
+
+function number(arg, name) {
+ _assert(arg, 'number', name);
+ if (!NDEBUG && (isNaN(arg) || !isFinite(arg))) {
+ throw new assert.AssertionError({
+ message: _(TYPE_REQUIRED, name, 'number'),
+ actual: arg,
+ expected: 'number',
+ operator: 'isNaN',
+ stackStartFunction: number
+ });
+ }
+}
+
+
+function object(arg, name) {
+ _assert(arg, 'object', name);
+}
+
+
+function stream(arg, name) {
+ _instanceof(arg, Stream, name);
+}
+
+
+function date(arg, name) {
+ _instanceof(arg, Date, name);
+}
+
+function regexp(arg, name) {
+ _instanceof(arg, RegExp, name);
+}
+
+
+function string(arg, name) {
+ _assert(arg, 'string', name);
+}
+
+
+function uuid(arg, name) {
+ string(arg, name);
+ if (!NDEBUG && !UUID_REGEXP.test(arg)) {
+ throw new assert.AssertionError({
+ message: _(TYPE_REQUIRED, name, 'uuid'),
+ actual: 'string',
+ expected: 'uuid',
+ operator: 'test',
+ stackStartFunction: uuid
+ });
+ }
+}
+
+
+///--- Exports
+
+module.exports = {
+ bool: bool,
+ buffer: buffer,
+ date: date,
+ func: func,
+ number: number,
+ object: object,
+ regexp: regexp,
+ stream: stream,
+ string: string,
+ uuid: uuid
+};
+
+
+Object.keys(module.exports).forEach(function (k) {
+ if (k === 'buffer')
+ return;
+
+ var name = 'arrayOf' + capitalize(k);
+
+ if (k === 'bool')
+ k = 'boolean';
+ if (k === 'func')
+ k = 'function';
+ module.exports[name] = function (arg, name) {
+ array(arg, k, name);
+ };
+});
+
+Object.keys(module.exports).forEach(function (k) {
+ var _name = 'optional' + capitalize(k);
+ var s = uncapitalize(k.replace('arrayOf', ''));
+ if (s === 'bool')
+ s = 'boolean';
+ if (s === 'func')
+ s = 'function';
+
+ if (k.indexOf('arrayOf') !== -1) {
+ module.exports[_name] = function (arg, name) {
+ if (!NDEBUG && arg !== undefined) {
+ array(arg, s, name);
+ }
+ };
+ } else {
+ module.exports[_name] = function (arg, name) {
+ if (!NDEBUG && arg !== undefined) {
+ _assert(arg, s, name);
+ }
+ };
+ }
+});
+
+
+// Reexport built-in assertions
+Object.keys(assert).forEach(function (k) {
+ if (k === 'AssertionError') {
+ module.exports[k] = assert[k];
+ return;
+ }
+
+ module.exports[k] = function () {
+ if (!NDEBUG) {
+ assert[k].apply(assert[k], arguments);
+ }
+ };
+});
--- /dev/null
+{
+ "author": {
+ "name": "Mark Cavage",
+ "email": "mcavage@gmail.com"
+ },
+ "name": "assert-plus",
+ "description": "Extra assertions on top of node's assert module",
+ "version": "0.1.5",
+ "main": "./assert.js",
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/mcavage/node-assert-plus.git"
+ },
+ "engines": {
+ "node": ">=0.8"
+ },
+ "bugs": {
+ "url": "https://github.com/mcavage/node-assert-plus/issues"
+ },
+ "dependencies": {},
+ "_id": "assert-plus@0.1.5",
+ "dist": {
+ "shasum": "ee74009413002d84cec7219c6ac811812e723160",
+ "tarball": "http://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz"
+ },
+ "_from": "assert-plus@>=0.1.5 <0.2.0",
+ "_npmVersion": "1.3.11",
+ "_npmUser": {
+ "name": "mcavage",
+ "email": "mcavage@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mcavage",
+ "email": "mcavage@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "ee74009413002d84cec7219c6ac811812e723160",
+ "_resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
+ "readme": "ERROR: No README data found!",
+ "homepage": "https://github.com/mcavage/node-assert-plus",
+ "scripts": {}
+}
--- /dev/null
+This contains tickets fixed in each version release in reverse chronological
+order. There is one ticket per line. Each commits message has the tickets fixed
+in it. The commit message also has the corresponding github issue. i.e. CTYPE-42
+would be issue 42. Each issue can be found at:
+https://github.com/rmustacc/node-ctype/issues/%d.
+
+v0.5.3
+CTYPE-50 Release 0.5.3
+Contributed by Nick Schonning:
+CTYPE-49 Add repository section to package.json
+Contributed by Jonathan Ong:
+CTYPE-48 Create .npmignore
+
+v0.5.2
+CTYPE-46 Release 0.5.2
+CTYPE-45 error in setEndian logic
+
+v0.5.1
+CTYPE-44 Release 0.5.1
+Contributed by Terin Stock:
+CTYPE-41 CTypeParser.writeStruct should return its offset
+Contributed by Terin Stock:
+CTYPE-42 int64_t returns wrong size
+
+v0.5.0
+CTYPE-40 Release 0.5.0
+CTYPE-39 want > 0.6 engine support
+
+v0.4.0
+CTYPE-37 Release v0.4.0
+CTYPE-6 want additional entry point for write
+CTYPE-20 Add 64-bit int support into core parser
+CTYPE-31 Fix bounds errors node/2129
+CTYPE-33 Update copyright holders
+CTYPE-34 ctf.js confuses sign bit.
+CTYPE-35 Make the README more useful for getting started
+CTYPE-36 want manual page on ctio functions
+
+v0.3.1
+CTYPE-29 Release 0.3.1
+CTYPE-28 Want v0.6 npm support
+
+v0.3.0
+CTYPE-27 Release v0.3.0
+CTYPE-26 Want alternate default char behavior
+
+v0.2.1
+CTYPE-25 Release v0.2.1
+CTYPE-24 Writing structs is busted
+
+v0.2.0:
+CTYPE-23 Release v0.2.0
+CTYPE-21 Add support for CTF JSON data
+CTYPE-22 Add Javascriptlint profile
+CTYPE-15 Pull in ctio updates from node/master
+
+v0.1.0:
+CTYPE-18 Bump version to v0.1.0
+CTYPE-17 Fix nested structures
+CTYPE-16 Remove extraneous logging
+CTYPE-14 toAbs64 and toApprox64 are not exported
+
+v0.0.3:
+CTYPE-12 Bump version to v0.0.3
+CTYPE-11 fix typo in wuint64
+CTYPE-10 Integrate jsstyle
+
+v0.0.2:
+CTYPE-8 dump npm version to v0.0.2
+CTYPE-9 want changelog
+CTYPE-7 fix typo in detypes.
+
+v0.0.1:
+CTYPE-5 Missing from NPM registry
+CTYPE-4 int16_t calls wrong read function
+CTYPE-3 API example types are missing quotes as strings
+CTYPE-2 doc missing 64-bit functions
+CTYPE-1 Need license
--- /dev/null
+The following license applies to all files unless the file is specified below.
+Each file specified below has its license information embedded in it:
+
+tools/jsstyle
+
+Copyright 2011, Robert Mustacchi. All rights reserved.
+Copyright 2011, Joyent, Inc. 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.
--- /dev/null
+Node-CType is a way to read and write binary data in structured and easy to use
+format. Its name comes from the C header file.
+
+To get started, simply clone the repository or use npm to install it. Once it is
+there, simply require it.
+
+git clone git://github.com/rmustacc/node-ctype
+npm install ctype
+var mod_ctype = require('ctype')
+
+
+There are two APIs that you can use, depending on what abstraction you'd like.
+The low level API let's you read and write individual integers and floats from
+buffers. The higher level API let's you read and write structures of these. To
+illustrate this, let's looks look at how we would read and write a binary
+encoded x,y point.
+
+In C we would define this structure as follows:
+
+typedef struct point {
+ uint16_t p_x;
+ uint16_t p_y;
+} point_t;
+
+To read a binary encoded point from a Buffer, we first need to create a CType
+parser (where we specify the endian and other options) and add the typedef.
+
+var parser = new mod_ctype.Parser({ endian: 'big' });
+parser.typedef('point_t', [
+ { x: { type: 'uint16_t' } },
+ { y: { type: 'uint16_t' } }
+]);
+
+From here, given a buffer buf and an offset into it, we can read a point.
+
+var out = parser.readData([ { point: { type: 'point_t' } } ], buffer, 0);
+console.log(out);
+{ point: { x: 23, y: 42 } }
+
+Another way to get the same information would be to use the low level methods.
+Note that these require you to manually deal with the offset. Here's how we'd
+get the same values of x and y from the buffer.
+
+var x = mod_ctype.ruint16(buf, 'big', 0);
+var y = mod_ctype.ruint16(buf, 'big', 2);
+console.log(x + ', ' + y);
+23, 42
+
+The true power of this API comes from the ability to define and nest typedefs,
+just as you would in C. By default, the following types are defined by default.
+Note that they return a Number, unless indicated otherwise.
+
+ * int8_t
+ * int16_t
+ * int32_t
+ * int64_t (returns an array where val[0] << 32 + val[1] would be the value)
+ * uint8_t
+ * uint16_t
+ * uint32_t
+ * uint64_t (returns an array where val[0] << 32 + val[1] would be the value)
+ * float
+ * double
+ * char (either returns a buffer with that character or a uint8_t)
+ * char[] (returns an object with the buffer and the number of characters read which is either the total amount requested or until the first 0)
+
+
+ctf2json integration:
+
+Node-CType supports consuming the output of ctf2json. Once you read in a JSON file,
+all you have to do to add all the definitions it contains is:
+
+var data, parser;
+data = JSON.parse(parsedJSONData);
+parser = mod_ctype.parseCTF(data, { endian: 'big' });
+
+For more documentation, see the file README.old. Full documentation is in the
+process of being rewritten as a series of manual pages which will be available
+in the repository and online for viewing.
+
+To read the ctio manual page simple run, from the root of the workspace:
+
+man -Mman -s 3ctype ctio
--- /dev/null
+This library provides a way to read and write binary data.
+
+Node CType is a way to read and write binary data in structured and easy to use
+formats. It's name comes from the header file, though it does not share as much
+with it as it perhaps should.
+
+There are two levels of the API. One is the raw API which everything is built on
+top of, while the other provides a much nicer abstraction and is built entirely
+by using the lower level API. The hope is that the low level API is both clear
+and useful. The low level API gets it's names from stdint.h (a rather
+appropriate source). The lower level API is presented at the end of this
+document.
+
+Standard CType API
+
+The CType interface is presented as a parser object that controls the
+endianness combined with a series of methods to change that value, parse and
+write out buffers, and a way to provide typedefs. Standard Types
+
+The CType parser supports the following basic types which return Numbers except
+as indicated:
+
+ * int8_t
+ * int16_t
+ * int32_t
+ * int64_t (returns an array where val[0] << 32 + val[1] would be the value)
+ * uint8_t
+ * uint16_t
+ * uint32_t
+ * uint64_t (returns an array where val[0] << 32 + val[1] would be the value)
+ * float
+ * double
+ * char (returns a buffer with just that single character)
+ * char[] (returns an object with the buffer and the number of characters read which is either the total amount requested or until the first 0)
+
+Specifying Structs
+
+The CType parser also supports the notion of structs. A struct is an array of
+JSON objects that defines an order of keys which have types and values. One
+would build a struct to represent a point (x,y) as follows:
+
+[
+ { x: { type: 'int16_t' }},
+ { y: { type: 'int16_t' }}
+]
+
+When this is passed into the read routine, it would read the first two bytes
+(as defined by int16_t) to determine the Number to use for X, and then it would
+read the next two bytes to determine the value of Y. When read this could
+return something like:
+
+{
+ x: 42,
+ y: -23
+}
+
+When someone wants to write values, we use the same format as above, but with
+additional value field:
+
+[
+ { x: { type: 'int16_t', value: 42 }},
+ { y: { type: 'int16_t', value: -23 }}
+]
+
+Now, the structure above may be optionally annotated with offsets. This tells
+us to rather than read continuously we should read the given value at the
+specified offset. If an offset is provided, it is is effectively the equivalent
+of lseek(offset, SEEK_SET). Thus, subsequent values will be read from that
+offset and incremented by the appropriate value. As an example:
+
+[
+ { x: { type: 'int16_t' }},
+ { y: { type: 'int16_t', offset: 20 }},
+ { z: { type: 'int16_t' }}
+]
+
+We would read x from the first starting offset given to us, for the sake of
+example, let's assume that's 0. After reading x, the next offset to read from
+would be 2; however, y specifies an offset, thus we jump directly to that
+offset and read y from byte 20. We would then read z from byte 22.
+
+The same offsets may be used when writing values.
+
+Typedef
+
+The basic set of types while covers the basics, is somewhat limiting. To make
+this richer, there is functionality to typedef something like in C. One can use
+typedef to add a new name to an existing type or to define a name to refer to a
+struct. Thus the following are all examples of a typedef:
+
+typedef('size_t', 'uint32_t');
+typedef('ssize_t', 'int32_t');
+typedef('point_t', [
+ { x: { type: 'int16_t' }},
+ { y: { type: 'int16_t' }}
+]);
+
+Once something has been typedef'd it can be used in any of the definitions
+previously shown.
+
+One cannot remove a typedef once created, this is analogous to C.
+
+The set of defined types can be printed with lstypes. The format of this output
+is subject to change, but likely will look something like:
+
+> lstypes();
+{
+ size_t: 'uint32_t',
+ ssize_t: 'int32_t',
+ point_t: [
+ { x: { type: 'int16_t' }},
+ { y: { type: 'int16_t' }}
+ ]
+}
+
+Specifying arrays
+
+Arrays can be specified by appending []s to a type. Arrays must have the size
+specified. The size must be specified and it can be done in one of two ways:
+
+ * An explicit non-zero integer size
+ * A name of a previously declared variable in the struct whose value is a
+ number.
+
+Note, that when using the name of a variable, it should be the string name for
+the key. This is only valid inside structs and the value must be declared
+before the value with the array. The following are examples:
+
+[
+ { ip_addr4: { type: 'uint8_t[4]' }},
+ { len: { type: 'uint32_t' }},
+ { data: { type: 'uint8_t[len]' }}
+]
+
+Arrays are permitted in typedefs; however, they must have a declared integer
+size. The following are examples of valid and invalid arrays:
+
+typedef('path', 'char[1024]'); /* Good */
+typedef('path', 'char[len]'); /* Bad! */
+
+64 bit values:
+
+Unfortunately Javascript represents values with a double, so you lose precision
+and the ability to represent Integers roughly beyond 2^53. To alleviate this, I
+propose the following for returning 64 bit integers when read:
+
+value[2]: Each entry is a 32 bit number which can be reconstructed to the
+original by the following formula:
+
+value[0] << 32 + value[1] (Note this will not work in Javascript)
+
+CTF JSON data:
+
+node-ctype can also handle JSON data that mathces the format described in the
+documentation of the tool ctf2json. Given the JSON data which specifies type
+information, it will transform that into a parser that understands all of the
+types defined inside of it. This is useful for more complicated structures that
+have a lot of typedefs.
+
+Interface overview
+
+The following is the header-file like interface to the parser object:
+
+/*
+ * Create a new instance of the parser. Each parser has its own store of
+ * typedefs and endianness. Conf is an object with the following values:
+ *
+ * endian Either 'big' or 'little' do determine the endianness we
+ * want to read from or write to.
+ *
+ */
+function CTypeParser(conf);
+
+/*
+ * Parses the CTF JSON data and creates a parser that understands all of those
+ * types.
+ *
+ * data Parsed JSON data that maches that CTF JSON
+ * specification.
+ *
+ * conf The configuration object to create a new CTypeParser
+ * from.
+ */
+CTypeParser parseCTF(data, conf);
+
+/*
+ * This is what we were born to do. We read the data from a buffer and return it
+ * in an object whose keys match the values from the object.
+ *
+ * def The array definition of the data to read in
+ *
+ * buffer The buffer to read data from
+ *
+ * offset The offset to start writing to
+ *
+ * Returns an object where each key corresponds to an entry in def and the value
+ * is the read value.
+ */
+Object CTypeParser.readData(<Type Definition>, buffer, offset);
+
+/*
+ * This is the second half of what we were born to do, write out the data
+ * itself.
+ *
+ * def The array definition of the data to write out with
+ * values
+ *
+ * buffer The buffer to write to
+ *
+ * offset The offset in the buffer to write to
+ */
+void CTypeParser.writeData(<Type Definition>, buffer, offset);
+
+/*
+ * A user has requested to add a type, let us honor their request. Yet, if their
+ * request doth spurn us, send them unto the Hells which Dante describes.
+ *
+ * name The string for the type definition we're adding
+ *
+ * value Either a string that is a type/array name or an object
+ * that describes a struct.
+ */
+void CTypeParser.prototype.typedef(name, value);
+
+Object CTypeParser.prototype.lstypes();
+
+/*
+ * Get the endian value for the current parser
+ */
+String CTypeParser.prototype.getEndian();
+
+/*
+ * Sets the current endian value for the Parser. If the value is not valid,
+ * throws an Error.
+ *
+ * endian Either 'big' or 'little' do determine the endianness we
+ * want to read from or write to.
+ *
+ */
+void CTypeParser.protoype.setEndian(String);
+
+/*
+ * Attempts to convert an array of two integers returned from rsint64 / ruint64
+ * into an absolute 64 bit number. If however the value would exceed 2^52 this
+ * will instead throw an error. The mantissa in a double is a 52 bit number and
+ * rather than potentially give you a value that is an approximation this will
+ * error. If you would rather an approximation, please see toApprox64.
+ *
+ * val An array of two 32-bit integers
+ */
+Number function toAbs64(val)
+
+/*
+ * Will return the 64 bit value as returned in an array from rsint64 / ruint64
+ * to a value as close as it can. Note that Javascript stores all numbers as a
+ * double and the mantissa only has 52 bits. Thus this version may approximate
+ * the value.
+ *
+ * val An array of two 32-bit integers
+ */
+Number function toApprox64(val)
+
+Low Level API
+
+The following function are provided at the low level:
+
+Read unsigned integers from a buffer:
+Number ruint8(buffer, endian, offset);
+Number ruint16(buffer, endian, offset);
+Number ruint32(buffer, endian, offset);
+Number[] ruint64(buffer, endian, offset);
+
+Read signed integers from a buffer:
+Number rsint8(buffer, endian, offset);
+Number rsint16(buffer, endian, offset);
+Number rsint32(buffer, endian, offset);
+Number[] rsint64(buffer, endian, offset);
+
+Read floating point numbers from a buffer:
+Number rfloat(buffer, endian, offset); /* IEEE-754 Single precision */
+Number rdouble(buffer, endian, offset); /* IEEE-754 Double precision */
+
+Write unsigned integers to a buffer:
+void wuint8(Number, endian, buffer, offset);
+void wuint16(Number, endian, buffer, offset);
+void wuint32(Number, endian, buffer, offset);
+void wuint64(Number[], endian, buffer, offset);
+
+Write signed integers from a buffer:
+void wsint8(Number, endian, buffer, offset);
+void wsint16(Number, endian, buffer, offset);
+void wsint32(Number, endian, buffer, offset);
+void wsint64(Number[], endian, buffer offset);
+
+Write floating point numbers from a buffer:
+void wfloat(Number, buffer, endian, offset); /* IEEE-754 Single precision */
+void wdouble(Number, buffer, endian, offset); /* IEEE-754 Double precision */
+
--- /dev/null
+/*
+ * ctf.js
+ *
+ * Understand and parse all of the different JSON formats of CTF data and
+ * translate that into a series of node-ctype friendly pieces. The reason for
+ * the abstraction is to handle different changes in the file format.
+ *
+ * We have to be careful here that we don't end up using a name that is already
+ * a built in type.
+ */
+var mod_assert = require('assert');
+var ASSERT = mod_assert.ok;
+
+var ctf_versions = [ '1.0' ];
+var ctf_entries = [ 'integer', 'float', 'typedef', 'struct' ];
+var ctf_deftypes = [ 'int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t',
+ 'uint32_t', 'float', 'double' ];
+
+function ctfParseInteger(entry, ctype)
+{
+ var name, sign, len, type;
+
+ name = entry['name'];
+ if (!('signed' in entry['integer']))
+ throw (new Error('Malformed CTF JSON: integer missing ' +
+ 'signed value'));
+
+
+ if (!('length' in entry['integer']))
+ throw (new Error('Malformed CTF JSON: integer missing ' +
+ 'length value'));
+
+ sign = entry['integer']['signed'];
+ len = entry['integer']['length'];
+ type = null;
+
+ if (sign && len == 1)
+ type = 'int8_t';
+ else if (len == 1)
+ type = 'uint8_t';
+ else if (sign && len == 2)
+ type = 'int16_t';
+ else if (len == 2)
+ type = 'uint16_t';
+ else if (sign && len == 4)
+ type = 'int32_t';
+ else if (len == 4)
+ type = 'uint32_t';
+ else if (sign && len == 8)
+ type = 'int64_t';
+ else if (len == 8)
+ type = 'uint64_t';
+
+ if (type === null)
+ throw (new Error('Malformed CTF JSON: integer has ' +
+ 'unsupported length and sign - ' + len + '/' + sign));
+
+ /*
+ * This means that this is the same as one of our built in types. If
+ * that's the case defining it would be an error. So instead of trying
+ * to typedef it, we'll return here.
+ */
+ if (name == type)
+ return;
+
+ if (name == 'char') {
+ ASSERT(type == 'int8_t');
+ return;
+ }
+
+ ctype.typedef(name, type);
+}
+
+function ctfParseFloat(entry, ctype)
+{
+ var name, len;
+
+ name = entry['name'];
+ if (!('length' in entry['float']))
+ throw (new Error('Malformed CTF JSON: float missing ' +
+ 'length value'));
+
+ len = entry['float']['length'];
+ if (len != 4 && len != 8)
+ throw (new Error('Malformed CTF JSON: float has invalid ' +
+ 'length value'));
+
+ if (len == 4) {
+ if (name == 'float')
+ return;
+ ctype.typedef(name, 'float');
+ } else if (len == 8) {
+ if (name == 'double')
+ return;
+ ctype.typedef(name, 'double');
+ }
+}
+
+function ctfParseTypedef(entry, ctype)
+{
+ var name, type, ii;
+
+ name = entry['name'];
+ if (typeof (entry['typedef']) != 'string')
+ throw (new Error('Malformed CTF JSON: typedef value in not ' +
+ 'a string'));
+
+ type = entry['typedef'];
+
+ /*
+ * We need to ensure that we're not looking at type that's one of our
+ * built in types. Traditionally in C a uint32_t would be a typedef to
+ * some kind of integer. However, those size types are built ins.
+ */
+ for (ii = 0; ii < ctf_deftypes.length; ii++) {
+ if (name == ctf_deftypes[ii])
+ return;
+ }
+
+ ctype.typedef(name, type);
+}
+
+function ctfParseStruct(entry, ctype)
+{
+ var name, type, ii, val, index, member, push;
+
+ member = [];
+ if (!Array.isArray(entry['struct']))
+ throw (new Error('Malformed CTF JSON: struct value is not ' +
+ 'an array'));
+
+ for (ii = 0; ii < entry['struct'].length; ii++) {
+ val = entry['struct'][ii];
+ if (!('name' in val))
+ throw (new Error('Malformed CTF JSON: struct member ' +
+ 'missing name'));
+
+ if (!('type' in val))
+ throw (new Error('Malformed CTF JSON: struct member ' +
+ 'missing type'));
+
+ if (typeof (val['name']) != 'string')
+ throw (new Error('Malformed CTF JSON: struct member ' +
+ 'name isn\'t a string'));
+
+ if (typeof (val['type']) != 'string')
+ throw (new Error('Malformed CTF JSON: struct member ' +
+ 'type isn\'t a string'));
+
+ /*
+ * CTF version 2 specifies array names as <type> [<num>] where
+ * as node-ctype does this as <type>[<num>].
+ */
+ name = val['name'];
+ type = val['type'];
+ index = type.indexOf(' [');
+ if (index != -1) {
+ type = type.substring(0, index) +
+ type.substring(index + 1, type.length);
+ }
+ push = {};
+ push[name] = { 'type': type };
+ member.push(push);
+ }
+
+ name = entry['name'];
+ ctype.typedef(name, member);
+}
+
+function ctfParseEntry(entry, ctype)
+{
+ var ii, found;
+
+ if (!('name' in entry))
+ throw (new Error('Malformed CTF JSON: entry missing "name" ' +
+ 'section'));
+
+ for (ii = 0; ii < ctf_entries.length; ii++) {
+ if (ctf_entries[ii] in entry)
+ found++;
+ }
+
+ if (found === 0)
+ throw (new Error('Malformed CTF JSON: found no entries'));
+
+ if (found >= 2)
+ throw (new Error('Malformed CTF JSON: found more than one ' +
+ 'entry'));
+
+ if ('integer' in entry) {
+ ctfParseInteger(entry, ctype);
+ return;
+ }
+
+ if ('float' in entry) {
+ ctfParseFloat(entry, ctype);
+ return;
+ }
+
+ if ('typedef' in entry) {
+ ctfParseTypedef(entry, ctype);
+ return;
+ }
+
+ if ('struct' in entry) {
+ ctfParseStruct(entry, ctype);
+ return;
+ }
+
+ ASSERT(false, 'shouldn\'t reach here');
+}
+
+function ctfParseJson(json, ctype)
+{
+ var version, ii;
+
+ ASSERT(json);
+ ASSERT(ctype);
+ if (!('metadata' in json))
+ throw (new Error('Invalid CTF JSON: missing metadata section'));
+
+ if (!('ctf2json_version' in json['metadata']))
+ throw (new Error('Invalid CTF JSON: missing ctf2json_version'));
+
+ version = json['metadata']['ctf2json_version'];
+ for (ii = 0; ii < ctf_versions.length; ii++) {
+ if (ctf_versions[ii] == version)
+ break;
+ }
+
+ if (ii == ctf_versions.length)
+ throw (new Error('Unsuported ctf2json_version: ' + version));
+
+ if (!('data' in json))
+ throw (new Error('Invalid CTF JSON: missing data section'));
+
+ if (!Array.isArray(json['data']))
+ throw (new Error('Malformed CTF JSON: data section is not ' +
+ 'an array'));
+
+ for (ii = 0; ii < json['data'].length; ii++)
+ ctfParseEntry(json['data'][ii], ctype);
+}
+
+exports.ctfParseJson = ctfParseJson;
--- /dev/null
+/*
+ * rm - Feb 2011
+ * ctio.js:
+ *
+ * A simple way to read and write simple ctypes. Of course, as you'll find the
+ * code isn't as simple as it might appear. The following types are currently
+ * supported in big and little endian formats:
+ *
+ * uint8_t int8_t
+ * uint16_t int16_t
+ * uint32_t int32_t
+ * float (single precision IEEE 754)
+ * double (double precision IEEE 754)
+ *
+ * This is designed to work in Node and v8. It may in fact work in other
+ * Javascript interpreters (that'd be pretty neat), but it hasn't been tested.
+ * If you find that it does in fact work, that's pretty cool. Try and pass word
+ * back to the original author.
+ *
+ * Note to the reader: If you're tabstop isn't set to 8, parts of this may look
+ * weird.
+ */
+
+/*
+ * Numbers in Javascript have a secret: all numbers must be represented with an
+ * IEEE-754 double. The double has a mantissa with a length of 52 bits with an
+ * implicit one. Thus the range of integers that can be represented is limited
+ * to the size of the mantissa, this makes reading and writing 64-bit integers
+ * difficult, but far from impossible.
+ *
+ * Another side effect of this representation is what happens when you use the
+ * bitwise operators, i.e. shift left, shift right, and, or, etc. In Javascript,
+ * each operand and the result is cast to a signed 32-bit number. However, in
+ * the case of >>> the values are cast to an unsigned number.
+ */
+
+/*
+ * A reminder on endian related issues:
+ *
+ * Big Endian: MSB -> First byte
+ * Little Endian: MSB->Last byte
+ */
+var mod_assert = require('assert');
+
+/*
+ * An 8 bit unsigned integer involves doing no significant work.
+ */
+function ruint8(buffer, endian, offset)
+{
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ return (buffer[offset]);
+}
+
+/*
+ * For 16 bit unsigned numbers we can do all the casting that we want to do.
+ */
+function rgint16(buffer, endian, offset)
+{
+ var val = 0;
+
+ if (endian == 'big') {
+ val = buffer[offset] << 8;
+ val |= buffer[offset+1];
+ } else {
+ val = buffer[offset];
+ val |= buffer[offset+1] << 8;
+ }
+
+ return (val);
+
+}
+
+function ruint16(buffer, endian, offset)
+{
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 1 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ return (rgint16(buffer, endian, offset));
+}
+
+/*
+ * Because most bitshifting is done using signed numbers, if we would go into
+ * the realm where we use that 32nd bit, we'll end up going into the negative
+ * range. i.e.:
+ * > 200 << 24
+ * -939524096
+ *
+ * Not the value you'd expect. To work around this, we end up having to do some
+ * abuse of the JavaScript standard. in this case, we know that a >>> shift is
+ * defined to cast our value to an *unsigned* 32-bit number. Because of that, we
+ * use that instead to save us some additional math, though it does feel a
+ * little weird and it isn't obvious as to why you woul dwant to do this at
+ * first.
+ */
+function rgint32(buffer, endian, offset)
+{
+ var val = 0;
+
+ if (endian == 'big') {
+ val = buffer[offset+1] << 16;
+ val |= buffer[offset+2] << 8;
+ val |= buffer[offset+3];
+ val = val + (buffer[offset] << 24 >>> 0);
+ } else {
+ val = buffer[offset+2] << 16;
+ val |= buffer[offset+1] << 8;
+ val |= buffer[offset];
+ val = val + (buffer[offset + 3] << 24 >>> 0);
+ }
+
+ return (val);
+}
+
+function ruint32(buffer, endian, offset)
+{
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ return (rgint32(buffer, endian, offset));
+}
+
+/*
+ * Reads a 64-bit unsigned number. The astue observer will note that this
+ * doesn't quite work. Javascript has chosen to only have numbers that can be
+ * represented by a double. A double only has 52 bits of mantissa with an
+ * implicit 1, thus we have up to 53 bits to represent an integer. However, 2^53
+ * doesn't quite give us what we want. Isn't 53 bits enough for anyone? What
+ * could you have possibly wanted to represent that was larger than that? Oh,
+ * maybe a size? You mean we bypassed the 4 GB limit on file sizes, when did
+ * that happen?
+ *
+ * To get around this egregious language issue, we're going to instead construct
+ * an array of two 32 bit unsigned integers. Where arr[0] << 32 + arr[1] would
+ * give the actual number. However, note that the above code probably won't
+ * produce the desired results because of the way Javascript numbers are
+ * doubles.
+ */
+function rgint64(buffer, endian, offset)
+{
+ var val = new Array(2);
+
+ if (endian == 'big') {
+ val[0] = ruint32(buffer, endian, offset);
+ val[1] = ruint32(buffer, endian, offset+4);
+ } else {
+ val[0] = ruint32(buffer, endian, offset+4);
+ val[1] = ruint32(buffer, endian, offset);
+ }
+
+ return (val);
+}
+
+function ruint64(buffer, endian, offset)
+{
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 7 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ return (rgint64(buffer, endian, offset));
+}
+
+
+/*
+ * Signed integer types, yay team! A reminder on how two's complement actually
+ * works. The first bit is the signed bit, i.e. tells us whether or not the
+ * number should be positive or negative. If the two's complement value is
+ * positive, then we're done, as it's equivalent to the unsigned representation.
+ *
+ * Now if the number is positive, you're pretty much done, you can just leverage
+ * the unsigned translations and return those. Unfortunately, negative numbers
+ * aren't quite that straightforward.
+ *
+ * At first glance, one might be inclined to use the traditional formula to
+ * translate binary numbers between the positive and negative values in two's
+ * complement. (Though it doesn't quite work for the most negative value)
+ * Mainly:
+ * - invert all the bits
+ * - add one to the result
+ *
+ * Of course, this doesn't quite work in Javascript. Take for example the value
+ * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of
+ * course, Javascript will do the following:
+ *
+ * > ~0xff80
+ * -65409
+ *
+ * Whoh there, Javascript, that's not quite right. But wait, according to
+ * Javascript that's perfectly correct. When Javascript ends up seeing the
+ * constant 0xff80, it has no notion that it is actually a signed number. It
+ * assumes that we've input the unsigned value 0xff80. Thus, when it does the
+ * binary negation, it casts it into a signed value, (positive 0xff80). Then
+ * when you perform binary negation on that, it turns it into a negative number.
+ *
+ * Instead, we're going to have to use the following general formula, that works
+ * in a rather Javascript friendly way. I'm glad we don't support this kind of
+ * weird numbering scheme in the kernel.
+ *
+ * (BIT-MAX - (unsigned)val + 1) * -1
+ *
+ * The astute observer, may think that this doesn't make sense for 8-bit numbers
+ * (really it isn't necessary for them). However, when you get 16-bit numbers,
+ * you do. Let's go back to our prior example and see how this will look:
+ *
+ * (0xffff - 0xff80 + 1) * -1
+ * (0x007f + 1) * -1
+ * (0x0080) * -1
+ *
+ * Doing it this way ends up allowing us to treat it appropriately in
+ * Javascript. Sigh, that's really quite ugly for what should just be a few bit
+ * shifts, ~ and &.
+ */
+
+/*
+ * Endianness doesn't matter for 8-bit signed values. We could in fact optimize
+ * this case because the more traditional methods work, but for consistency,
+ * we'll keep doing this the same way.
+ */
+function rsint8(buffer, endian, offset)
+{
+ var neg;
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ neg = buffer[offset] & 0x80;
+ if (!neg)
+ return (buffer[offset]);
+
+ return ((0xff - buffer[offset] + 1) * -1);
+}
+
+/*
+ * The 16-bit version requires a bit more effort. In this case, we can leverage
+ * our unsigned code to generate the value we want to return.
+ */
+function rsint16(buffer, endian, offset)
+{
+ var neg, val;
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 1 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = rgint16(buffer, endian, offset);
+ neg = val & 0x8000;
+ if (!neg)
+ return (val);
+
+ return ((0xffff - val + 1) * -1);
+}
+
+/*
+ * We really shouldn't leverage our 32-bit code here and instead utilize the
+ * fact that we know that since these are signed numbers, we can do all the
+ * shifting and binary anding to generate the 32-bit number. But, for
+ * consistency we'll do the same. If we want to do otherwise, we should instead
+ * make the 32 bit unsigned code do the optimization. But as long as there
+ * aren't floats secretly under the hood for that, we /should/ be okay.
+ */
+function rsint32(buffer, endian, offset)
+{
+ var neg, val;
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = rgint32(buffer, endian, offset);
+ neg = val & 0x80000000;
+ if (!neg)
+ return (val);
+
+ return ((0xffffffff - val + 1) * -1);
+}
+
+/*
+ * The signed version of this code suffers from all of the same problems of the
+ * other 64 bit version.
+ */
+function rsint64(buffer, endian, offset)
+{
+ var neg, val;
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = rgint64(buffer, endian, offset);
+ neg = val[0] & 0x80000000;
+
+ if (!neg)
+ return (val);
+
+ val[0] = (0xffffffff - val[0]) * -1;
+ val[1] = (0xffffffff - val[1] + 1) * -1;
+
+ /*
+ * If we had the key 0x8000000000000000, that would leave the lower 32
+ * bits as 0xffffffff, however, since we're goint to add one, that would
+ * actually leave the lower 32-bits as 0x100000000, which would break
+ * our ability to write back a value that we received. To work around
+ * this, if we actually get that value, we're going to bump the upper
+ * portion by 1 and set this to zero.
+ */
+ mod_assert.ok(val[1] <= 0x100000000);
+ if (val[1] == -0x100000000) {
+ val[1] = 0;
+ val[0]--;
+ }
+
+ return (val);
+}
+
+/*
+ * We now move onto IEEE 754: The traditional form for floating point numbers
+ * and what is secretly hiding at the heart of everything in this. I really hope
+ * that someone is actually using this, as otherwise, this effort is probably
+ * going to be more wasted.
+ *
+ * One might be tempted to use parseFloat here, but that wouldn't work at all
+ * for several reasons. Mostly due to the way floats actually work, and
+ * parseFloat only actually works in base 10. I don't see base 10 anywhere near
+ * this file.
+ *
+ * In this case we'll implement the single and double precision versions. The
+ * quadruple precision, while probably useful, wouldn't really be accepted by
+ * Javascript, so let's not even waste our time.
+ *
+ * So let's review how this format looks like. A single precision value is 32
+ * bits and has three parts:
+ * - Sign bit
+ * - Exponent (Using bias notation)
+ * - Mantissa
+ *
+ * |s|eeeeeeee|mmmmmmmmmmmmmmmmmmmmmmmmm|
+ * 31| 30-23 | 22 - 0 |
+ *
+ * The exponent is stored in a biased input. The bias in this case 127.
+ * Therefore, our exponent is equal to the 8-bit value - 127.
+ *
+ * By default, a number is normalized in IEEE, that means that the mantissa has
+ * an implicit one that we don't see. So really the value stored is 1.m.
+ * However, if the exponent is all zeros, then instead we have to shift
+ * everything to the right one and there is no more implicit one.
+ *
+ * Special values:
+ * - Positive Infinity:
+ * Sign: 0
+ * Exponent: All 1s
+ * Mantissa: 0
+ * - Negative Infinity:
+ * Sign: 1
+ * Exponent: All 1s
+ * Mantissa: 0
+ * - NaN:
+ * Sign: *
+ * Exponent: All 1s
+ * Mantissa: non-zero
+ * - Zero:
+ * Sign: *
+ * Exponent: All 0s
+ * Mantissa: 0
+ *
+ * In the case of zero, the sign bit determines whether we get a positive or
+ * negative zero. However, since Javascript cannot determine the difference
+ * between the two: i.e. -0 == 0, we just always return 0.
+ *
+ */
+function rfloat(buffer, endian, offset)
+{
+ var bytes = [];
+ var sign, exponent, mantissa, val;
+ var bias = 127;
+ var maxexp = 0xff;
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ /* Normalize the bytes to be in endian order */
+ if (endian == 'big') {
+ bytes[0] = buffer[offset];
+ bytes[1] = buffer[offset+1];
+ bytes[2] = buffer[offset+2];
+ bytes[3] = buffer[offset+3];
+ } else {
+ bytes[3] = buffer[offset];
+ bytes[2] = buffer[offset+1];
+ bytes[1] = buffer[offset+2];
+ bytes[0] = buffer[offset+3];
+ }
+
+ sign = bytes[0] & 0x80;
+ exponent = (bytes[0] & 0x7f) << 1;
+ exponent |= (bytes[1] & 0x80) >>> 7;
+ mantissa = (bytes[1] & 0x7f) << 16;
+ mantissa |= bytes[2] << 8;
+ mantissa |= bytes[3];
+
+ /* Check for special cases before we do general parsing */
+ if (!sign && exponent == maxexp && mantissa === 0)
+ return (Number.POSITIVE_INFINITY);
+
+ if (sign && exponent == maxexp && mantissa === 0)
+ return (Number.NEGATIVE_INFINITY);
+
+ if (exponent == maxexp && mantissa !== 0)
+ return (Number.NaN);
+
+ /*
+ * Javascript really doesn't have support for positive or negative zero.
+ * So we're not going to try and give it to you. That would be just
+ * plain weird. Besides -0 == 0.
+ */
+ if (exponent === 0 && mantissa === 0)
+ return (0);
+
+ /*
+ * Now we can deal with the bias and the determine whether the mantissa
+ * has the implicit one or not.
+ */
+ exponent -= bias;
+ if (exponent == -bias) {
+ exponent++;
+ val = 0;
+ } else {
+ val = 1;
+ }
+
+ val = (val + mantissa * Math.pow(2, -23)) * Math.pow(2, exponent);
+
+ if (sign)
+ val *= -1;
+
+ return (val);
+}
+
+/*
+ * Doubles in IEEE 754 are like their brothers except for a few changes and
+ * increases in size:
+ * - The exponent is now 11 bits
+ * - The mantissa is now 52 bits
+ * - The bias is now 1023
+ *
+ * |s|eeeeeeeeeee|mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm|
+ * 63| 62 - 52 | 51 - 0 |
+ * 63| 62 - 52 | 51 - 0 |
+ *
+ * While the size has increased a fair amount, we're going to end up keeping the
+ * same general formula for calculating the final value. As a reminder, this
+ * formula is:
+ *
+ * (-1)^s * (n + m) * 2^(e-b)
+ *
+ * Where:
+ * s is the sign bit
+ * n is (exponent > 0) ? 1 : 0 -- Determines whether we're normalized
+ * or not
+ * m is the mantissa
+ * e is the exponent specified
+ * b is the bias for the exponent
+ *
+ */
+function rdouble(buffer, endian, offset)
+{
+ var bytes = [];
+ var sign, exponent, mantissa, val, lowmant;
+ var bias = 1023;
+ var maxexp = 0x7ff;
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 7 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ /* Normalize the bytes to be in endian order */
+ if (endian == 'big') {
+ bytes[0] = buffer[offset];
+ bytes[1] = buffer[offset+1];
+ bytes[2] = buffer[offset+2];
+ bytes[3] = buffer[offset+3];
+ bytes[4] = buffer[offset+4];
+ bytes[5] = buffer[offset+5];
+ bytes[6] = buffer[offset+6];
+ bytes[7] = buffer[offset+7];
+ } else {
+ bytes[7] = buffer[offset];
+ bytes[6] = buffer[offset+1];
+ bytes[5] = buffer[offset+2];
+ bytes[4] = buffer[offset+3];
+ bytes[3] = buffer[offset+4];
+ bytes[2] = buffer[offset+5];
+ bytes[1] = buffer[offset+6];
+ bytes[0] = buffer[offset+7];
+ }
+
+ /*
+ * We can construct the exponent and mantissa the same way as we did in
+ * the case of a float, just increase the range of the exponent.
+ */
+ sign = bytes[0] & 0x80;
+ exponent = (bytes[0] & 0x7f) << 4;
+ exponent |= (bytes[1] & 0xf0) >>> 4;
+
+ /*
+ * This is going to be ugly but then again, we're dealing with IEEE 754.
+ * This could probably be done as a node add on in a few lines of C++,
+ * but oh we'll, we've made it this far so let's be native the rest of
+ * the way...
+ *
+ * What we're going to do is break the mantissa into two parts, the
+ * lower 24 bits and the upper 28 bits. We'll multiply the upper 28 bits
+ * by the appropriate power and then add in the lower 24-bits. Not
+ * really that great. It's pretty much a giant kludge to deal with
+ * Javascript eccentricities around numbers.
+ */
+ lowmant = bytes[7];
+ lowmant |= bytes[6] << 8;
+ lowmant |= bytes[5] << 16;
+ mantissa = bytes[4];
+ mantissa |= bytes[3] << 8;
+ mantissa |= bytes[2] << 16;
+ mantissa |= (bytes[1] & 0x0f) << 24;
+ mantissa *= Math.pow(2, 24); /* Equivalent to << 24, but JS compat */
+ mantissa += lowmant;
+
+ /* Check for special cases before we do general parsing */
+ if (!sign && exponent == maxexp && mantissa === 0)
+ return (Number.POSITIVE_INFINITY);
+
+ if (sign && exponent == maxexp && mantissa === 0)
+ return (Number.NEGATIVE_INFINITY);
+
+ if (exponent == maxexp && mantissa !== 0)
+ return (Number.NaN);
+
+ /*
+ * Javascript really doesn't have support for positive or negative zero.
+ * So we're not going to try and give it to you. That would be just
+ * plain weird. Besides -0 == 0.
+ */
+ if (exponent === 0 && mantissa === 0)
+ return (0);
+
+ /*
+ * Now we can deal with the bias and the determine whether the mantissa
+ * has the implicit one or not.
+ */
+ exponent -= bias;
+ if (exponent == -bias) {
+ exponent++;
+ val = 0;
+ } else {
+ val = 1;
+ }
+
+ val = (val + mantissa * Math.pow(2, -52)) * Math.pow(2, exponent);
+
+ if (sign)
+ val *= -1;
+
+ return (val);
+}
+
+/*
+ * Now that we have gone through the pain of reading the individual types, we're
+ * probably going to want some way to write these back. None of this is going to
+ * be good. But since we have Javascript numbers this should certainly be more
+ * interesting. Though we can constrain this end a little bit more in what is
+ * valid. For now, let's go back to our friends the unsigned value.
+ */
+
+/*
+ * Unsigned numbers seem deceptively easy. Here are the general steps and rules
+ * that we are going to take:
+ * - If the number is negative, throw an Error
+ * - Truncate any floating point portion
+ * - Take the modulus of the number in our base
+ * - Write it out to the buffer in the endian format requested at the offset
+ */
+
+/*
+ * We have to make sure that the value is a valid integer. This means that it is
+ * non-negative. It has no fractional component and that it does not exceed the
+ * maximum allowed value.
+ *
+ * value The number to check for validity
+ *
+ * max The maximum value
+ */
+function prepuint(value, max)
+{
+ if (typeof (value) != 'number')
+ throw (new (Error('cannot write a non-number as a number')));
+
+ if (value < 0)
+ throw (new Error('specified a negative value for writing an ' +
+ 'unsigned value'));
+
+ if (value > max)
+ throw (new Error('value is larger than maximum value for ' +
+ 'type'));
+
+ if (Math.floor(value) !== value)
+ throw (new Error('value has a fractional component'));
+
+ return (value);
+}
+
+/*
+ * 8-bit version, classy. We can ignore endianness which is good.
+ */
+function wuint8(value, endian, buffer, offset)
+{
+ var val;
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = prepuint(value, 0xff);
+ buffer[offset] = val;
+}
+
+/*
+ * Pretty much the same as the 8-bit version, just this time we need to worry
+ * about endian related issues.
+ */
+function wgint16(val, endian, buffer, offset)
+{
+ if (endian == 'big') {
+ buffer[offset] = (val & 0xff00) >>> 8;
+ buffer[offset+1] = val & 0x00ff;
+ } else {
+ buffer[offset+1] = (val & 0xff00) >>> 8;
+ buffer[offset] = val & 0x00ff;
+ }
+}
+
+function wuint16(value, endian, buffer, offset)
+{
+ var val;
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 1 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = prepuint(value, 0xffff);
+ wgint16(val, endian, buffer, offset);
+}
+
+/*
+ * The 32-bit version is going to have to be a little different unfortunately.
+ * We can't quite bitshift to get the largest byte, because that would end up
+ * getting us caught by the signed values.
+ *
+ * And yes, we do want to subtract out the lower part by default. This means
+ * that when we do the division, it will be treated as a bit shift and we won't
+ * end up generating a floating point value. If we did generate a floating point
+ * value we'd have to truncate it intelligently, this saves us that problem and
+ * may even be somewhat faster under the hood.
+ */
+function wgint32(val, endian, buffer, offset)
+{
+ if (endian == 'big') {
+ buffer[offset] = (val - (val & 0x00ffffff)) / Math.pow(2, 24);
+ buffer[offset+1] = (val >>> 16) & 0xff;
+ buffer[offset+2] = (val >>> 8) & 0xff;
+ buffer[offset+3] = val & 0xff;
+ } else {
+ buffer[offset+3] = (val - (val & 0x00ffffff)) /
+ Math.pow(2, 24);
+ buffer[offset+2] = (val >>> 16) & 0xff;
+ buffer[offset+1] = (val >>> 8) & 0xff;
+ buffer[offset] = val & 0xff;
+ }
+}
+
+function wuint32(value, endian, buffer, offset)
+{
+ var val;
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = prepuint(value, 0xffffffff);
+ wgint32(val, endian, buffer, offset);
+}
+
+/*
+ * Unlike the other versions, we expect the value to be in the form of two
+ * arrays where value[0] << 32 + value[1] would result in the value that we
+ * want.
+ */
+function wgint64(value, endian, buffer, offset)
+{
+ if (endian == 'big') {
+ wgint32(value[0], endian, buffer, offset);
+ wgint32(value[1], endian, buffer, offset+4);
+ } else {
+ wgint32(value[0], endian, buffer, offset+4);
+ wgint32(value[1], endian, buffer, offset);
+ }
+}
+
+function wuint64(value, endian, buffer, offset)
+{
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (!(value instanceof Array))
+ throw (new Error('value must be an array'));
+
+ if (value.length != 2)
+ throw (new Error('value must be an array of length 2'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 7 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ prepuint(value[0], 0xffffffff);
+ prepuint(value[1], 0xffffffff);
+ wgint64(value, endian, buffer, offset);
+}
+
+/*
+ * We now move onto our friends in the signed number category. Unlike unsigned
+ * numbers, we're going to have to worry a bit more about how we put values into
+ * arrays. Since we are only worrying about signed 32-bit values, we're in
+ * slightly better shape. Unfortunately, we really can't do our favorite binary
+ * & in this system. It really seems to do the wrong thing. For example:
+ *
+ * > -32 & 0xff
+ * 224
+ *
+ * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of
+ * this aren't treated as a signed number. Ultimately a bad thing.
+ *
+ * What we're going to want to do is basically create the unsigned equivalent of
+ * our representation and pass that off to the wuint* functions. To do that
+ * we're going to do the following:
+ *
+ * - if the value is positive
+ * we can pass it directly off to the equivalent wuint
+ * - if the value is negative
+ * we do the following computation:
+ * mb + val + 1, where
+ * mb is the maximum unsigned value in that byte size
+ * val is the Javascript negative integer
+ *
+ *
+ * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If
+ * you do out the computations:
+ *
+ * 0xffff - 128 + 1
+ * 0xffff - 127
+ * 0xff80
+ *
+ * You can then encode this value as the signed version. This is really rather
+ * hacky, but it should work and get the job done which is our goal here.
+ *
+ * Thus the overall flow is:
+ * - Truncate the floating point part of the number
+ * - We don't have to take the modulus, because the unsigned versions will
+ * take care of that for us. And we don't have to worry about that
+ * potentially causing bad things to happen because of sign extension
+ * - Pass it off to the appropriate unsigned version, potentially modifying
+ * the negative portions as necessary.
+ */
+
+/*
+ * A series of checks to make sure we actually have a signed 32-bit number
+ */
+function prepsint(value, max, min)
+{
+ if (typeof (value) != 'number')
+ throw (new (Error('cannot write a non-number as a number')));
+
+ if (value > max)
+ throw (new Error('value larger than maximum allowed value'));
+
+ if (value < min)
+ throw (new Error('value smaller than minimum allowed value'));
+
+ if (Math.floor(value) !== value)
+ throw (new Error('value has a fractional component'));
+
+ return (value);
+}
+
+/*
+ * The 8-bit version of the signed value. Overall, fairly straightforward.
+ */
+function wsint8(value, endian, buffer, offset)
+{
+ var val;
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = prepsint(value, 0x7f, -0x80);
+ if (val >= 0)
+ wuint8(val, endian, buffer, offset);
+ else
+ wuint8(0xff + val + 1, endian, buffer, offset);
+}
+
+/*
+ * The 16-bit version of the signed value. Also, fairly straightforward.
+ */
+function wsint16(value, endian, buffer, offset)
+{
+ var val;
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 1 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = prepsint(value, 0x7fff, -0x8000);
+ if (val >= 0)
+ wgint16(val, endian, buffer, offset);
+ else
+ wgint16(0xffff + val + 1, endian, buffer, offset);
+
+}
+
+/*
+ * We can do this relatively easily by leveraging the code used for 32-bit
+ * unsigned code.
+ */
+function wsint32(value, endian, buffer, offset)
+{
+ var val;
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ val = prepsint(value, 0x7fffffff, -0x80000000);
+ if (val >= 0)
+ wgint32(val, endian, buffer, offset);
+ else
+ wgint32(0xffffffff + val + 1, endian, buffer, offset);
+}
+
+/*
+ * The signed 64 bit integer should by in the same format as when received.
+ * Mainly it should ensure that the value is an array of two integers where
+ * value[0] << 32 + value[1] is the desired number. Furthermore, the two values
+ * need to be equal.
+ */
+function wsint64(value, endian, buffer, offset)
+{
+ var vzpos, vopos;
+ var vals = new Array(2);
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (!(value instanceof Array))
+ throw (new Error('value must be an array'));
+
+ if (value.length != 2)
+ throw (new Error('value must be an array of length 2'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+ if (offset + 7 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ /*
+ * We need to make sure that we have the same sign on both values. The
+ * hokiest way to to do this is to multiply the number by +inf. If we do
+ * this, we'll get either +/-inf depending on the sign of the value.
+ * Once we have this, we can compare it to +inf to see if the number is
+ * positive or not.
+ */
+ vzpos = (value[0] * Number.POSITIVE_INFINITY) ==
+ Number.POSITIVE_INFINITY;
+ vopos = (value[1] * Number.POSITIVE_INFINITY) ==
+ Number.POSITIVE_INFINITY;
+
+ /*
+ * If either of these is zero, then we don't actually need this check.
+ */
+ if (value[0] != 0 && value[1] != 0 && vzpos != vopos)
+ throw (new Error('Both entries in the array must have ' +
+ 'the same sign'));
+
+ /*
+ * Doing verification for a signed 64-bit integer is actually a big
+ * trickier than it appears. We can't quite use our standard techniques
+ * because we need to compare both sets of values. The first value is
+ * pretty straightforward. If the first value is beond the extremes than
+ * we error out. However, the valid range of the second value varies
+ * based on the first one. If the first value is negative, and *not* the
+ * largest negative value, than it can be any integer within the range [
+ * 0, 0xffffffff ]. If it is the largest negative number, it must be
+ * zero.
+ *
+ * If the first number is positive, than it doesn't matter what the
+ * value is. We just simply have to make sure we have a valid positive
+ * integer.
+ */
+ if (vzpos) {
+ prepuint(value[0], 0x7fffffff);
+ prepuint(value[1], 0xffffffff);
+ } else {
+ prepsint(value[0], 0, -0x80000000);
+ prepsint(value[1], 0, -0xffffffff);
+ if (value[0] == -0x80000000 && value[1] != 0)
+ throw (new Error('value smaller than minimum ' +
+ 'allowed value'));
+ }
+
+ /* Fix negative numbers */
+ if (value[0] < 0 || value[1] < 0) {
+ vals[0] = 0xffffffff - Math.abs(value[0]);
+ vals[1] = 0x100000000 - Math.abs(value[1]);
+ if (vals[1] == 0x100000000) {
+ vals[1] = 0;
+ vals[0]++;
+ }
+ } else {
+ vals[0] = value[0];
+ vals[1] = value[1];
+ }
+ wgint64(vals, endian, buffer, offset);
+}
+
+/*
+ * Now we are moving onto the weirder of these, the float and double. For this
+ * we're going to just have to do something that's pretty weird. First off, we
+ * have no way to get at the underlying float representation, at least not
+ * easily. But that doesn't mean we can't figure it out, we just have to use our
+ * heads.
+ *
+ * One might propose to use Number.toString(2). Of course, this is not really
+ * that good, because the ECMAScript 262 v3 Standard says the following Section
+ * 15.7.4.2-Number.prototype.toString (radix):
+ *
+ * If radix is an integer from 2 to 36, but not 10, the result is a string, the
+ * choice of which is implementation-dependent.
+ *
+ * Well that doesn't really help us one bit now does it? We could use the
+ * standard base 10 version of the string, but that's just going to create more
+ * errors as we end up trying to convert it back to a binary value. So, really
+ * this just means we have to be non-lazy and parse the structure intelligently.
+ *
+ * First off, we can do the basic checks: NaN, positive and negative infinity.
+ *
+ * Now that those are done we can work backwards to generate the mantissa and
+ * exponent.
+ *
+ * The first thing we need to do is determine the sign bit, easy to do, check
+ * whether the value is less than 0. And convert the number to its absolute
+ * value representation. Next, we need to determine if the value is less than
+ * one or greater than or equal to one and from there determine what power was
+ * used to get there. What follows is now specific to floats, though the general
+ * ideas behind this will hold for doubles as well, but the exact numbers
+ * involved will change.
+ *
+ * Once we have that power we can determine the exponent and the mantissa. Call
+ * the value that has the number of bits to reach the power ebits. In the
+ * general case they have the following values:
+ *
+ * exponent 127 + ebits
+ * mantissa value * 2^(23 - ebits) & 0x7fffff
+ *
+ * In the case where the value of ebits is <= -127 we are now in the case where
+ * we no longer have normalized numbers. In this case the values take on the
+ * following values:
+ *
+ * exponent 0
+ * mantissa value * 2^149 & 0x7fffff
+ *
+ * Once we have the values for the sign, mantissa, and exponent. We reconstruct
+ * the four bytes as follows:
+ *
+ * byte0 sign bit and seven most significant bits from the exp
+ * sign << 7 | (exponent & 0xfe) >>> 1
+ *
+ * byte1 lsb from the exponent and 7 top bits from the mantissa
+ * (exponent & 0x01) << 7 | (mantissa & 0x7f0000) >>> 16
+ *
+ * byte2 bits 8-15 (zero indexing) from mantissa
+ * mantissa & 0xff00 >> 8
+ *
+ * byte3 bits 0-7 from mantissa
+ * mantissa & 0xff
+ *
+ * Once we have this we have to assign them into the buffer in proper endian
+ * order.
+ */
+
+/*
+ * Compute the log base 2 of the value. Now, someone who remembers basic
+ * properties of logarithms will point out that we could use the change of base
+ * formula for logs, and in fact that would be astute, because that's what we'll
+ * do for now. It feels cleaner, albeit it may be less efficient than just
+ * iterating and dividing by 2. We may want to come back and revisit that some
+ * day.
+ */
+function log2(value)
+{
+ return (Math.log(value) / Math.log(2));
+}
+
+/*
+ * Helper to determine the exponent of the number we're looking at.
+ */
+function intexp(value)
+{
+ return (Math.floor(log2(value)));
+}
+
+/*
+ * Helper to determine the exponent of the fractional part of the value.
+ */
+function fracexp(value)
+{
+ return (Math.floor(log2(value)));
+}
+
+function wfloat(value, endian, buffer, offset)
+{
+ var sign, exponent, mantissa, ebits;
+ var bytes = [];
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+
+ if (offset + 3 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ if (isNaN(value)) {
+ sign = 0;
+ exponent = 0xff;
+ mantissa = 23;
+ } else if (value == Number.POSITIVE_INFINITY) {
+ sign = 0;
+ exponent = 0xff;
+ mantissa = 0;
+ } else if (value == Number.NEGATIVE_INFINITY) {
+ sign = 1;
+ exponent = 0xff;
+ mantissa = 0;
+ } else {
+ /* Well we have some work to do */
+
+ /* Thankfully the sign bit is trivial */
+ if (value < 0) {
+ sign = 1;
+ value = Math.abs(value);
+ } else {
+ sign = 0;
+ }
+
+ /* Use the correct function to determine number of bits */
+ if (value < 1)
+ ebits = fracexp(value);
+ else
+ ebits = intexp(value);
+
+ /* Time to deal with the issues surrounding normalization */
+ if (ebits <= -127) {
+ exponent = 0;
+ mantissa = (value * Math.pow(2, 149)) & 0x7fffff;
+ } else {
+ exponent = 127 + ebits;
+ mantissa = value * Math.pow(2, 23 - ebits);
+ mantissa &= 0x7fffff;
+ }
+ }
+
+ bytes[0] = sign << 7 | (exponent & 0xfe) >>> 1;
+ bytes[1] = (exponent & 0x01) << 7 | (mantissa & 0x7f0000) >>> 16;
+ bytes[2] = (mantissa & 0x00ff00) >>> 8;
+ bytes[3] = mantissa & 0x0000ff;
+
+ if (endian == 'big') {
+ buffer[offset] = bytes[0];
+ buffer[offset+1] = bytes[1];
+ buffer[offset+2] = bytes[2];
+ buffer[offset+3] = bytes[3];
+ } else {
+ buffer[offset] = bytes[3];
+ buffer[offset+1] = bytes[2];
+ buffer[offset+2] = bytes[1];
+ buffer[offset+3] = bytes[0];
+ }
+}
+
+/*
+ * Now we move onto doubles. Doubles are similar to floats in pretty much all
+ * ways except that the processing isn't quite as straightforward because we
+ * can't always use shifting, i.e. we have > 32 bit values.
+ *
+ * We're going to proceed in an identical fashion to floats and utilize the same
+ * helper functions. All that really is changing are the specific values that we
+ * use to do the calculations. Thus, to review we have to do the following.
+ *
+ * First get the sign bit and convert the value to its absolute value
+ * representation. Next, we determine the number of bits that we used to get to
+ * the value, branching whether the value is greater than or less than 1. Once
+ * we have that value which we will again call ebits, we have to do the
+ * following in the general case:
+ *
+ * exponent 1023 + ebits
+ * mantissa [value * 2^(52 - ebits)] % 2^52
+ *
+ * In the case where the value of ebits <= -1023 we no longer use normalized
+ * numbers, thus like with floats we have to do slightly different processing:
+ *
+ * exponent 0
+ * mantissa [value * 2^1074] % 2^52
+ *
+ * Once we have determined the sign, exponent and mantissa we can construct the
+ * bytes as follows:
+ *
+ * byte0 sign bit and seven most significant bits form the exp
+ * sign << 7 | (exponent & 0x7f0) >>> 4
+ *
+ * byte1 Remaining 4 bits from the exponent and the four most
+ * significant bits from the mantissa 48-51
+ * (exponent & 0x00f) << 4 | mantissa >>> 48
+ *
+ * byte2 Bits 40-47 from the mantissa
+ * (mantissa >>> 40) & 0xff
+ *
+ * byte3 Bits 32-39 from the mantissa
+ * (mantissa >>> 32) & 0xff
+ *
+ * byte4 Bits 24-31 from the mantissa
+ * (mantissa >>> 24) & 0xff
+ *
+ * byte5 Bits 16-23 from the Mantissa
+ * (mantissa >>> 16) & 0xff
+ *
+ * byte6 Bits 8-15 from the mantissa
+ * (mantissa >>> 8) & 0xff
+ *
+ * byte7 Bits 0-7 from the mantissa
+ * mantissa & 0xff
+ *
+ * Now we can't quite do the right shifting that we want in bytes 1 - 3, because
+ * we'll have extended too far and we'll lose those values when we try and do
+ * the shift. Instead we have to use an alternate approach. To try and stay out
+ * of floating point, what we'll do is say that mantissa -= bytes[4-7] and then
+ * divide by 2^32. Once we've done that we can use binary arithmetic. Oof,
+ * that's ugly, but it seems to avoid using floating point (just based on how v8
+ * seems to be optimizing for base 2 arithmetic).
+ */
+function wdouble(value, endian, buffer, offset)
+{
+ var sign, exponent, mantissa, ebits;
+ var bytes = [];
+
+ if (value === undefined)
+ throw (new Error('missing value'));
+
+ if (endian === undefined)
+ throw (new Error('missing endian'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset'));
+
+
+ if (offset + 7 >= buffer.length)
+ throw (new Error('Trying to read beyond buffer length'));
+
+ if (isNaN(value)) {
+ sign = 0;
+ exponent = 0x7ff;
+ mantissa = 23;
+ } else if (value == Number.POSITIVE_INFINITY) {
+ sign = 0;
+ exponent = 0x7ff;
+ mantissa = 0;
+ } else if (value == Number.NEGATIVE_INFINITY) {
+ sign = 1;
+ exponent = 0x7ff;
+ mantissa = 0;
+ } else {
+ /* Well we have some work to do */
+
+ /* Thankfully the sign bit is trivial */
+ if (value < 0) {
+ sign = 1;
+ value = Math.abs(value);
+ } else {
+ sign = 0;
+ }
+
+ /* Use the correct function to determine number of bits */
+ if (value < 1)
+ ebits = fracexp(value);
+ else
+ ebits = intexp(value);
+
+ /*
+ * This is a total hack to determine a denormalized value.
+ * Unfortunately, we sometimes do not get a proper value for
+ * ebits, i.e. we lose the values that would get rounded off.
+ *
+ *
+ * The astute observer may wonder why we would be
+ * multiplying by two Math.pows rather than just summing
+ * them. Well, that's to get around a small bug in the
+ * way v8 seems to implement the function. On occasion
+ * doing:
+ *
+ * foo * Math.pow(2, 1023 + 51)
+ *
+ * Causes us to overflow to infinity, where as doing:
+ *
+ * foo * Math.pow(2, 1023) * Math.pow(2, 51)
+ *
+ * Does not cause us to overflow. Go figure.
+ *
+ */
+ if (value <= 2.225073858507201e-308 || ebits <= -1023) {
+ exponent = 0;
+ mantissa = value * Math.pow(2, 1023) * Math.pow(2, 51);
+ mantissa %= Math.pow(2, 52);
+ } else {
+ /*
+ * We might have gotten fucked by our floating point
+ * logarithm magic. This is rather crappy, but that's
+ * our luck. If we just had a log base 2 or access to
+ * the stupid underlying representation this would have
+ * been much easier and we wouldn't have such stupid
+ * kludges or hacks.
+ */
+ if (ebits > 1023)
+ ebits = 1023;
+ exponent = 1023 + ebits;
+ mantissa = value * Math.pow(2, -ebits);
+ mantissa *= Math.pow(2, 52);
+ mantissa %= Math.pow(2, 52);
+ }
+ }
+
+ /* Fill the bytes in backwards to deal with the size issues */
+ bytes[7] = mantissa & 0xff;
+ bytes[6] = (mantissa >>> 8) & 0xff;
+ bytes[5] = (mantissa >>> 16) & 0xff;
+ mantissa = (mantissa - (mantissa & 0xffffff)) / Math.pow(2, 24);
+ bytes[4] = mantissa & 0xff;
+ bytes[3] = (mantissa >>> 8) & 0xff;
+ bytes[2] = (mantissa >>> 16) & 0xff;
+ bytes[1] = (exponent & 0x00f) << 4 | mantissa >>> 24;
+ bytes[0] = (sign << 7) | (exponent & 0x7f0) >>> 4;
+
+ if (endian == 'big') {
+ buffer[offset] = bytes[0];
+ buffer[offset+1] = bytes[1];
+ buffer[offset+2] = bytes[2];
+ buffer[offset+3] = bytes[3];
+ buffer[offset+4] = bytes[4];
+ buffer[offset+5] = bytes[5];
+ buffer[offset+6] = bytes[6];
+ buffer[offset+7] = bytes[7];
+ } else {
+ buffer[offset+7] = bytes[0];
+ buffer[offset+6] = bytes[1];
+ buffer[offset+5] = bytes[2];
+ buffer[offset+4] = bytes[3];
+ buffer[offset+3] = bytes[4];
+ buffer[offset+2] = bytes[5];
+ buffer[offset+1] = bytes[6];
+ buffer[offset] = bytes[7];
+ }
+}
+
+/*
+ * Actually export our work above. One might argue that we shouldn't expose
+ * these interfaces and just force people to use the higher level abstractions
+ * around this work. However, unlike say other libraries we've come across, this
+ * interface has several properties: it makes sense, it's simple, and it's
+ * useful.
+ */
+exports.ruint8 = ruint8;
+exports.ruint16 = ruint16;
+exports.ruint32 = ruint32;
+exports.ruint64 = ruint64;
+exports.wuint8 = wuint8;
+exports.wuint16 = wuint16;
+exports.wuint32 = wuint32;
+exports.wuint64 = wuint64;
+
+exports.rsint8 = rsint8;
+exports.rsint16 = rsint16;
+exports.rsint32 = rsint32;
+exports.rsint64 = rsint64;
+exports.wsint8 = wsint8;
+exports.wsint16 = wsint16;
+exports.wsint32 = wsint32;
+exports.wsint64 = wsint64;
+
+exports.rfloat = rfloat;
+exports.rdouble = rdouble;
+exports.wfloat = wfloat;
+exports.wdouble = wdouble;
--- /dev/null
+/*
+ * rm - Feb 2011
+ * ctype.js
+ *
+ * This module provides a simple abstraction towards reading and writing
+ * different types of binary data. It is designed to use ctio.js and provide a
+ * richer and more expressive API on top of it.
+ *
+ * By default we support the following as built in basic types:
+ * int8_t
+ * int16_t
+ * int32_t
+ * uint8_t
+ * uint16_t
+ * uint32_t
+ * uint64_t
+ * float
+ * double
+ * char
+ * char[]
+ *
+ * Each type is returned as a Number, with the exception of char and char[]
+ * which are returned as Node Buffers. A char is considered a uint8_t.
+ *
+ * Requests to read and write data are specified as an array of JSON objects.
+ * This is also the same way that one declares structs. Even if just a single
+ * value is requested, it must be done as a struct. The array order determines
+ * the order that we try and read values. Each entry has the following format
+ * with values marked with a * being optional.
+ *
+ * { key: { type: /type/, value*: /value/, offset*: /offset/ }
+ *
+ * If offset is defined, we lseek(offset, SEEK_SET) before reading the next
+ * value. Value is defined when we're writing out data, otherwise it's ignored.
+ *
+ */
+
+var mod_ctf = require('./ctf.js');
+var mod_ctio = require('./ctio.js');
+var mod_assert = require('assert');
+
+/*
+ * This is the set of basic types that we support.
+ *
+ * read The function to call to read in a value from a buffer
+ *
+ * write The function to call to write a value to a buffer
+ *
+ */
+var deftypes = {
+ 'uint8_t': { read: ctReadUint8, write: ctWriteUint8 },
+ 'uint16_t': { read: ctReadUint16, write: ctWriteUint16 },
+ 'uint32_t': { read: ctReadUint32, write: ctWriteUint32 },
+ 'uint64_t': { read: ctReadUint64, write: ctWriteUint64 },
+ 'int8_t': { read: ctReadSint8, write: ctWriteSint8 },
+ 'int16_t': { read: ctReadSint16, write: ctWriteSint16 },
+ 'int32_t': { read: ctReadSint32, write: ctWriteSint32 },
+ 'int64_t': { read: ctReadSint64, write: ctWriteSint64 },
+ 'float': { read: ctReadFloat, write: ctWriteFloat },
+ 'double': { read: ctReadDouble, write: ctWriteDouble },
+ 'char': { read: ctReadChar, write: ctWriteChar },
+ 'char[]': { read: ctReadCharArray, write: ctWriteCharArray }
+};
+
+/*
+ * The following are wrappers around the CType IO low level API. They encode
+ * knowledge about the size and return something in the expected format.
+ */
+function ctReadUint8(endian, buffer, offset)
+{
+ var val = mod_ctio.ruint8(buffer, endian, offset);
+ return ({ value: val, size: 1 });
+}
+
+function ctReadUint16(endian, buffer, offset)
+{
+ var val = mod_ctio.ruint16(buffer, endian, offset);
+ return ({ value: val, size: 2 });
+}
+
+function ctReadUint32(endian, buffer, offset)
+{
+ var val = mod_ctio.ruint32(buffer, endian, offset);
+ return ({ value: val, size: 4 });
+}
+
+function ctReadUint64(endian, buffer, offset)
+{
+ var val = mod_ctio.ruint64(buffer, endian, offset);
+ return ({ value: val, size: 8 });
+}
+
+function ctReadSint8(endian, buffer, offset)
+{
+ var val = mod_ctio.rsint8(buffer, endian, offset);
+ return ({ value: val, size: 1 });
+}
+
+function ctReadSint16(endian, buffer, offset)
+{
+ var val = mod_ctio.rsint16(buffer, endian, offset);
+ return ({ value: val, size: 2 });
+}
+
+function ctReadSint32(endian, buffer, offset)
+{
+ var val = mod_ctio.rsint32(buffer, endian, offset);
+ return ({ value: val, size: 4 });
+}
+
+function ctReadSint64(endian, buffer, offset)
+{
+ var val = mod_ctio.rsint64(buffer, endian, offset);
+ return ({ value: val, size: 8 });
+}
+
+function ctReadFloat(endian, buffer, offset)
+{
+ var val = mod_ctio.rfloat(buffer, endian, offset);
+ return ({ value: val, size: 4 });
+}
+
+function ctReadDouble(endian, buffer, offset)
+{
+ var val = mod_ctio.rdouble(buffer, endian, offset);
+ return ({ value: val, size: 8 });
+}
+
+/*
+ * Reads a single character into a node buffer
+ */
+function ctReadChar(endian, buffer, offset)
+{
+ var res = new Buffer(1);
+ res[0] = mod_ctio.ruint8(buffer, endian, offset);
+ return ({ value: res, size: 1 });
+}
+
+function ctReadCharArray(length, endian, buffer, offset)
+{
+ var ii;
+ var res = new Buffer(length);
+
+ for (ii = 0; ii < length; ii++)
+ res[ii] = mod_ctio.ruint8(buffer, endian, offset + ii);
+
+ return ({ value: res, size: length });
+}
+
+function ctWriteUint8(value, endian, buffer, offset)
+{
+ mod_ctio.wuint8(value, endian, buffer, offset);
+ return (1);
+}
+
+function ctWriteUint16(value, endian, buffer, offset)
+{
+ mod_ctio.wuint16(value, endian, buffer, offset);
+ return (2);
+}
+
+function ctWriteUint32(value, endian, buffer, offset)
+{
+ mod_ctio.wuint32(value, endian, buffer, offset);
+ return (4);
+}
+
+function ctWriteUint64(value, endian, buffer, offset)
+{
+ mod_ctio.wuint64(value, endian, buffer, offset);
+ return (8);
+}
+
+function ctWriteSint8(value, endian, buffer, offset)
+{
+ mod_ctio.wsint8(value, endian, buffer, offset);
+ return (1);
+}
+
+function ctWriteSint16(value, endian, buffer, offset)
+{
+ mod_ctio.wsint16(value, endian, buffer, offset);
+ return (2);
+}
+
+function ctWriteSint32(value, endian, buffer, offset)
+{
+ mod_ctio.wsint32(value, endian, buffer, offset);
+ return (4);
+}
+
+function ctWriteSint64(value, endian, buffer, offset)
+{
+ mod_ctio.wsint64(value, endian, buffer, offset);
+ return (8);
+}
+
+function ctWriteFloat(value, endian, buffer, offset)
+{
+ mod_ctio.wfloat(value, endian, buffer, offset);
+ return (4);
+}
+
+function ctWriteDouble(value, endian, buffer, offset)
+{
+ mod_ctio.wdouble(value, endian, buffer, offset);
+ return (8);
+}
+
+/*
+ * Writes a single character into a node buffer
+ */
+function ctWriteChar(value, endian, buffer, offset)
+{
+ if (!(value instanceof Buffer))
+ throw (new Error('Input must be a buffer'));
+
+ mod_ctio.ruint8(value[0], endian, buffer, offset);
+ return (1);
+}
+
+/*
+ * We're going to write 0s into the buffer if the string is shorter than the
+ * length of the array.
+ */
+function ctWriteCharArray(value, length, endian, buffer, offset)
+{
+ var ii;
+
+ if (!(value instanceof Buffer))
+ throw (new Error('Input must be a buffer'));
+
+ if (value.length > length)
+ throw (new Error('value length greater than array length'));
+
+ for (ii = 0; ii < value.length && ii < length; ii++)
+ mod_ctio.wuint8(value[ii], endian, buffer, offset + ii);
+
+ for (; ii < length; ii++)
+ mod_ctio.wuint8(0, endian, offset + ii);
+
+
+ return (length);
+}
+
+/*
+ * Each parser has their own set of types. We want to make sure that they each
+ * get their own copy as they may need to modify it.
+ */
+function ctGetBasicTypes()
+{
+ var ret = {};
+ var key;
+ for (key in deftypes)
+ ret[key] = deftypes[key];
+
+ return (ret);
+}
+
+/*
+ * Given a string in the form of type[length] we want to split this into an
+ * object that extracts that information. We want to note that we could possibly
+ * have nested arrays so this should only check the furthest one. It may also be
+ * the case that we have no [] pieces, in which case we just return the current
+ * type.
+ */
+function ctParseType(str)
+{
+ var begInd, endInd;
+ var type, len;
+ if (typeof (str) != 'string')
+ throw (new Error('type must be a Javascript string'));
+
+ endInd = str.lastIndexOf(']');
+ if (endInd == -1) {
+ if (str.lastIndexOf('[') != -1)
+ throw (new Error('found invalid type with \'[\' but ' +
+ 'no corresponding \']\''));
+
+ return ({ type: str });
+ }
+
+ begInd = str.lastIndexOf('[');
+ if (begInd == -1)
+ throw (new Error('found invalid type with \']\' but ' +
+ 'no corresponding \'[\''));
+
+ if (begInd >= endInd)
+ throw (new Error('malformed type, \']\' appears before \'[\''));
+
+ type = str.substring(0, begInd);
+ len = str.substring(begInd + 1, endInd);
+
+ return ({ type: type, len: len });
+}
+
+/*
+ * Given a request validate that all of the fields for it are valid and make
+ * sense. This includes verifying the following notions:
+ * - Each type requested is present in types
+ * - Only allow a name for a field to be specified once
+ * - If an array is specified, validate that the requested field exists and
+ * comes before it.
+ * - If fields is defined, check that each entry has the occurrence of field
+ */
+function ctCheckReq(def, types, fields)
+{
+ var ii, jj;
+ var req, keys, key;
+ var found = {};
+
+ if (!(def instanceof Array))
+ throw (new Error('definition is not an array'));
+
+ if (def.length === 0)
+ throw (new Error('definition must have at least one element'));
+
+ for (ii = 0; ii < def.length; ii++) {
+ req = def[ii];
+ if (!(req instanceof Object))
+ throw (new Error('definition must be an array of' +
+ 'objects'));
+
+ keys = Object.keys(req);
+ if (keys.length != 1)
+ throw (new Error('definition entry must only have ' +
+ 'one key'));
+
+ if (keys[0] in found)
+ throw (new Error('Specified name already ' +
+ 'specified: ' + keys[0]));
+
+ if (!('type' in req[keys[0]]))
+ throw (new Error('missing required type definition'));
+
+ key = ctParseType(req[keys[0]]['type']);
+
+ /*
+ * We may have nested arrays, we need to check the validity of
+ * the types until the len field is undefined in key. However,
+ * each time len is defined we need to verify it is either an
+ * integer or corresponds to an already seen key.
+ */
+ while (key['len'] !== undefined) {
+ if (isNaN(parseInt(key['len'], 10))) {
+ if (!(key['len'] in found))
+ throw (new Error('Given an array ' +
+ 'length without a matching type'));
+
+ }
+
+ key = ctParseType(key['type']);
+ }
+
+ /* Now we can validate if the type is valid */
+ if (!(key['type'] in types))
+ throw (new Error('type not found or typdefed: ' +
+ key['type']));
+
+ /* Check for any required fields */
+ if (fields !== undefined) {
+ for (jj = 0; jj < fields.length; jj++) {
+ if (!(fields[jj] in req[keys[0]]))
+ throw (new Error('Missing required ' +
+ 'field: ' + fields[jj]));
+ }
+ }
+
+ found[keys[0]] = true;
+ }
+}
+
+
+/*
+ * Create a new instance of the parser. Each parser has its own store of
+ * typedefs and endianness. Conf is an object with the following required
+ * values:
+ *
+ * endian Either 'big' or 'little' do determine the endianness we
+ * want to read from or write to.
+ *
+ * And the following optional values:
+ *
+ * char-type Valid options here are uint8 and int8. If uint8 is
+ * specified this changes the default behavior of a single
+ * char from being a buffer of a single character to being
+ * a uint8_t. If int8, it becomes an int8_t instead.
+ */
+function CTypeParser(conf)
+{
+ if (!conf) throw (new Error('missing required argument'));
+
+ if (!('endian' in conf))
+ throw (new Error('missing required endian value'));
+
+ if (conf['endian'] != 'big' && conf['endian'] != 'little')
+ throw (new Error('Invalid endian type'));
+
+ if ('char-type' in conf && (conf['char-type'] != 'uint8' &&
+ conf['char-type'] != 'int8'))
+ throw (new Error('invalid option for char-type: ' +
+ conf['char-type']));
+
+ this.endian = conf['endian'];
+ this.types = ctGetBasicTypes();
+
+ /*
+ * There may be a more graceful way to do this, but this will have to
+ * serve.
+ */
+ if ('char-type' in conf && conf['char-type'] == 'uint8')
+ this.types['char'] = this.types['uint8_t'];
+
+ if ('char-type' in conf && conf['char-type'] == 'int8')
+ this.types['char'] = this.types['int8_t'];
+}
+
+/*
+ * Sets the current endian value for the Parser. If the value is not valid,
+ * throws an Error.
+ *
+ * endian Either 'big' or 'little' do determine the endianness we
+ * want to read from or write to.
+ *
+ */
+CTypeParser.prototype.setEndian = function (endian)
+{
+ if (endian != 'big' && endian != 'little')
+ throw (new Error('invalid endian type, must be big or ' +
+ 'little'));
+
+ this.endian = endian;
+};
+
+/*
+ * Returns the current value of the endian value for the parser.
+ */
+CTypeParser.prototype.getEndian = function ()
+{
+ return (this.endian);
+};
+
+/*
+ * A user has requested to add a type, let us honor their request. Yet, if their
+ * request doth spurn us, send them unto the Hells which Dante describes.
+ *
+ * name The string for the type definition we're adding
+ *
+ * value Either a string that is a type/array name or an object
+ * that describes a struct.
+ */
+CTypeParser.prototype.typedef = function (name, value)
+{
+ var type;
+
+ if (name === undefined)
+ throw (new (Error('missing required typedef argument: name')));
+
+ if (value === undefined)
+ throw (new (Error('missing required typedef argument: value')));
+
+ if (typeof (name) != 'string')
+ throw (new (Error('the name of a type must be a string')));
+
+ type = ctParseType(name);
+
+ if (type['len'] !== undefined)
+ throw (new Error('Cannot have an array in the typedef name'));
+
+ if (name in this.types)
+ throw (new Error('typedef name already present: ' + name));
+
+ if (typeof (value) != 'string' && !(value instanceof Array))
+ throw (new Error('typedef value must either be a string or ' +
+ 'struct'));
+
+ if (typeof (value) == 'string') {
+ type = ctParseType(value);
+ if (type['len'] !== undefined) {
+ if (isNaN(parseInt(type['len'], 10)))
+ throw (new (Error('typedef value must use ' +
+ 'fixed size array when outside of a ' +
+ 'struct')));
+ }
+
+ this.types[name] = value;
+ } else {
+ /* We have a struct, validate it */
+ ctCheckReq(value, this.types);
+ this.types[name] = value;
+ }
+};
+
+/*
+ * Include all of the typedefs, but none of the built in types. This should be
+ * treated as read-only.
+ */
+CTypeParser.prototype.lstypes = function ()
+{
+ var key;
+ var ret = {};
+
+ for (key in this.types) {
+ if (key in deftypes)
+ continue;
+ ret[key] = this.types[key];
+ }
+
+ return (ret);
+};
+
+/*
+ * Given a type string that may have array types that aren't numbers, try and
+ * fill them in from the values object. The object should be of the format where
+ * indexing into it should return a number for that type.
+ *
+ * str The type string
+ *
+ * values An object that can be used to fulfill type information
+ */
+function ctResolveArray(str, values)
+{
+ var ret = '';
+ var type = ctParseType(str);
+
+ while (type['len'] !== undefined) {
+ if (isNaN(parseInt(type['len'], 10))) {
+ if (typeof (values[type['len']]) != 'number')
+ throw (new Error('cannot sawp in non-number ' +
+ 'for array value'));
+ ret = '[' + values[type['len']] + ']' + ret;
+ } else {
+ ret = '[' + type['len'] + ']' + ret;
+ }
+ type = ctParseType(type['type']);
+ }
+
+ ret = type['type'] + ret;
+
+ return (ret);
+}
+
+/*
+ * [private] Either the typedef resolves to another type string or to a struct.
+ * If it resolves to a struct, we just pass it off to read struct. If not, we
+ * can just pass it off to read entry.
+ */
+CTypeParser.prototype.resolveTypedef = function (type, dispatch, buffer,
+ offset, value)
+{
+ var pt;
+
+ mod_assert.ok(type in this.types);
+ if (typeof (this.types[type]) == 'string') {
+ pt = ctParseType(this.types[type]);
+ if (dispatch == 'read')
+ return (this.readEntry(pt, buffer, offset));
+ else if (dispatch == 'write')
+ return (this.writeEntry(value, pt, buffer, offset));
+ else
+ throw (new Error('invalid dispatch type to ' +
+ 'resolveTypedef'));
+ } else {
+ if (dispatch == 'read')
+ return (this.readStruct(this.types[type], buffer,
+ offset));
+ else if (dispatch == 'write')
+ return (this.writeStruct(value, this.types[type],
+ buffer, offset));
+ else
+ throw (new Error('invalid dispatch type to ' +
+ 'resolveTypedef'));
+ }
+
+};
+
+/*
+ * [private] Try and read in the specific entry.
+ */
+CTypeParser.prototype.readEntry = function (type, buffer, offset)
+{
+ var parse, len;
+
+ /*
+ * Because we want to special case char[]s this is unfortunately
+ * a bit uglier than it really should be. We want to special
+ * case char[]s so that we return a node buffer, thus they are a
+ * first class type where as all other arrays just call into a
+ * generic array routine which calls their data-specific routine
+ * the specified number of times.
+ *
+ * The valid dispatch options we have are:
+ * - Array and char => char[] handler
+ * - Generic array handler
+ * - Generic typedef handler
+ * - Basic type handler
+ */
+ if (type['len'] !== undefined) {
+ len = parseInt(type['len'], 10);
+ if (isNaN(len))
+ throw (new Error('somehow got a non-numeric length'));
+
+ if (type['type'] == 'char')
+ parse = this.types['char[]']['read'](len,
+ this.endian, buffer, offset);
+ else
+ parse = this.readArray(type['type'],
+ len, buffer, offset);
+ } else {
+ if (type['type'] in deftypes)
+ parse = this.types[type['type']]['read'](this.endian,
+ buffer, offset);
+ else
+ parse = this.resolveTypedef(type['type'], 'read',
+ buffer, offset);
+ }
+
+ return (parse);
+};
+
+/*
+ * [private] Read an array of data
+ */
+CTypeParser.prototype.readArray = function (type, length, buffer, offset)
+{
+ var ii, ent, pt;
+ var baseOffset = offset;
+ var ret = new Array(length);
+ pt = ctParseType(type);
+
+ for (ii = 0; ii < length; ii++) {
+ ent = this.readEntry(pt, buffer, offset);
+ offset += ent['size'];
+ ret[ii] = ent['value'];
+ }
+
+ return ({ value: ret, size: offset - baseOffset });
+};
+
+/*
+ * [private] Read a single struct in.
+ */
+CTypeParser.prototype.readStruct = function (def, buffer, offset)
+{
+ var parse, ii, type, entry, key;
+ var baseOffset = offset;
+ var ret = {};
+
+ /* Walk it and handle doing what's necessary */
+ for (ii = 0; ii < def.length; ii++) {
+ key = Object.keys(def[ii])[0];
+ entry = def[ii][key];
+
+ /* Resolve all array values */
+ type = ctParseType(ctResolveArray(entry['type'], ret));
+
+ if ('offset' in entry)
+ offset = baseOffset + entry['offset'];
+
+ parse = this.readEntry(type, buffer, offset);
+
+ offset += parse['size'];
+ ret[key] = parse['value'];
+ }
+
+ return ({ value: ret, size: (offset-baseOffset)});
+};
+
+/*
+ * This is what we were born to do. We read the data from a buffer and return it
+ * in an object whose keys match the values from the object.
+ *
+ * def The array definition of the data to read in
+ *
+ * buffer The buffer to read data from
+ *
+ * offset The offset to start writing to
+ *
+ * Returns an object where each key corresponds to an entry in def and the value
+ * is the read value.
+ */
+CTypeParser.prototype.readData = function (def, buffer, offset)
+{
+ /* Sanity check for arguments */
+ if (def === undefined)
+ throw (new Error('missing definition for what we should be' +
+ 'parsing'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer for what we should be ' +
+ 'parsing'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset for what we should be ' +
+ 'parsing'));
+
+ /* Sanity check the object definition */
+ ctCheckReq(def, this.types);
+
+ return (this.readStruct(def, buffer, offset)['value']);
+};
+
+/*
+ * [private] Write out an array of data
+ */
+CTypeParser.prototype.writeArray = function (value, type, length, buffer,
+ offset)
+{
+ var ii, pt;
+ var baseOffset = offset;
+ if (!(value instanceof Array))
+ throw (new Error('asked to write an array, but value is not ' +
+ 'an array'));
+
+ if (value.length != length)
+ throw (new Error('asked to write array of length ' + length +
+ ' but that does not match value length: ' + value.length));
+
+ pt = ctParseType(type);
+ for (ii = 0; ii < length; ii++)
+ offset += this.writeEntry(value[ii], pt, buffer, offset);
+
+ return (offset - baseOffset);
+};
+
+/*
+ * [private] Write the specific entry
+ */
+CTypeParser.prototype.writeEntry = function (value, type, buffer, offset)
+{
+ var len, ret;
+
+ if (type['len'] !== undefined) {
+ len = parseInt(type['len'], 10);
+ if (isNaN(len))
+ throw (new Error('somehow got a non-numeric length'));
+
+ if (type['type'] == 'char')
+ ret = this.types['char[]']['write'](value, len,
+ this.endian, buffer, offset);
+ else
+ ret = this.writeArray(value, type['type'],
+ len, buffer, offset);
+ } else {
+ if (type['type'] in deftypes)
+ ret = this.types[type['type']]['write'](value,
+ this.endian, buffer, offset);
+ else
+ ret = this.resolveTypedef(type['type'], 'write',
+ buffer, offset, value);
+ }
+
+ return (ret);
+};
+
+/*
+ * [private] Write a single struct out.
+ */
+CTypeParser.prototype.writeStruct = function (value, def, buffer, offset)
+{
+ var ii, entry, type, key;
+ var baseOffset = offset;
+ var vals = {};
+
+ for (ii = 0; ii < def.length; ii++) {
+ key = Object.keys(def[ii])[0];
+ entry = def[ii][key];
+
+ type = ctParseType(ctResolveArray(entry['type'], vals));
+
+ if ('offset' in entry)
+ offset = baseOffset + entry['offset'];
+
+ offset += this.writeEntry(value[ii], type, buffer, offset);
+ /* Now that we've written it out, we can use it for arrays */
+ vals[key] = value[ii];
+ }
+
+ return (offset);
+};
+
+/*
+ * Unfortunately, we're stuck with the sins of an initial poor design. Because
+ * of that, we are going to have to support the old way of writing data via
+ * writeData. There we insert the values that you want to write into the
+ * definition. A little baroque. Internally, we use the new model. So we need to
+ * just get those values out of there. But to maintain the principle of least
+ * surprise, we're not going to modify the input data.
+ */
+function getValues(def)
+{
+ var ii, out, key;
+ out = [];
+ for (ii = 0; ii < def.length; ii++) {
+ key = Object.keys(def[ii])[0];
+ mod_assert.ok('value' in def[ii][key]);
+ out.push(def[ii][key]['value']);
+ }
+
+ return (out);
+}
+
+/*
+ * This is the second half of what we were born to do, write out the data
+ * itself. Historically this function required you to put your values in the
+ * definition section. This was not the smartest thing to do and a bit of an
+ * oversight to be honest. As such, this function now takes a values argument.
+ * If values is non-null and non-undefined, it will be used to determine the
+ * values. This means that the old method is still supported, but is no longer
+ * acceptable.
+ *
+ * def The array definition of the data to write out with
+ * values
+ *
+ * buffer The buffer to write to
+ *
+ * offset The offset in the buffer to write to
+ *
+ * values An array of values to write.
+ */
+CTypeParser.prototype.writeData = function (def, buffer, offset, values)
+{
+ var hv;
+
+ if (def === undefined)
+ throw (new Error('missing definition for what we should be' +
+ 'parsing'));
+
+ if (buffer === undefined)
+ throw (new Error('missing buffer for what we should be ' +
+ 'parsing'));
+
+ if (offset === undefined)
+ throw (new Error('missing offset for what we should be ' +
+ 'parsing'));
+
+ hv = (values != null && values != undefined);
+ if (hv) {
+ if (!Array.isArray(values))
+ throw (new Error('missing values for writing'));
+ ctCheckReq(def, this.types);
+ } else {
+ ctCheckReq(def, this.types, [ 'value' ]);
+ }
+
+ this.writeStruct(hv ? values : getValues(def), def, buffer, offset);
+};
+
+/*
+ * Functions to go to and from 64 bit numbers in a way that is compatible with
+ * Javascript limitations. There are two sets. One where the user is okay with
+ * an approximation and one where they are definitely not okay with an
+ * approximation.
+ */
+
+/*
+ * Attempts to convert an array of two integers returned from rsint64 / ruint64
+ * into an absolute 64 bit number. If however the value would exceed 2^52 this
+ * will instead throw an error. The mantissa in a double is a 52 bit number and
+ * rather than potentially give you a value that is an approximation this will
+ * error. If you would rather an approximation, please see toApprox64.
+ *
+ * val An array of two 32-bit integers
+ */
+function toAbs64(val)
+{
+ if (val === undefined)
+ throw (new Error('missing required arg: value'));
+
+ if (!Array.isArray(val))
+ throw (new Error('value must be an array'));
+
+ if (val.length != 2)
+ throw (new Error('value must be an array of length 2'));
+
+ /* We have 20 bits worth of precision in this range */
+ if (val[0] >= 0x100000)
+ throw (new Error('value would become approximated'));
+
+ return (val[0] * Math.pow(2, 32) + val[1]);
+}
+
+/*
+ * Will return the 64 bit value as returned in an array from rsint64 / ruint64
+ * to a value as close as it can. Note that Javascript stores all numbers as a
+ * double and the mantissa only has 52 bits. Thus this version may approximate
+ * the value.
+ *
+ * val An array of two 32-bit integers
+ */
+function toApprox64(val)
+{
+ if (val === undefined)
+ throw (new Error('missing required arg: value'));
+
+ if (!Array.isArray(val))
+ throw (new Error('value must be an array'));
+
+ if (val.length != 2)
+ throw (new Error('value must be an array of length 2'));
+
+ return (Math.pow(2, 32) * val[0] + val[1]);
+}
+
+function parseCTF(json, conf)
+{
+ var ctype = new CTypeParser(conf);
+ mod_ctf.ctfParseJson(json, ctype);
+
+ return (ctype);
+}
+
+/*
+ * Export the few things we actually want to. Currently this is just the CType
+ * Parser and ctio.
+ */
+exports.Parser = CTypeParser;
+exports.toAbs64 = toAbs64;
+exports.toApprox64 = toApprox64;
+
+exports.parseCTF = parseCTF;
+
+exports.ruint8 = mod_ctio.ruint8;
+exports.ruint16 = mod_ctio.ruint16;
+exports.ruint32 = mod_ctio.ruint32;
+exports.ruint64 = mod_ctio.ruint64;
+exports.wuint8 = mod_ctio.wuint8;
+exports.wuint16 = mod_ctio.wuint16;
+exports.wuint32 = mod_ctio.wuint32;
+exports.wuint64 = mod_ctio.wuint64;
+
+exports.rsint8 = mod_ctio.rsint8;
+exports.rsint16 = mod_ctio.rsint16;
+exports.rsint32 = mod_ctio.rsint32;
+exports.rsint64 = mod_ctio.rsint64;
+exports.wsint8 = mod_ctio.wsint8;
+exports.wsint16 = mod_ctio.wsint16;
+exports.wsint32 = mod_ctio.wsint32;
+exports.wsint64 = mod_ctio.wsint64;
+
+exports.rfloat = mod_ctio.rfloat;
+exports.rdouble = mod_ctio.rdouble;
+exports.wfloat = mod_ctio.wfloat;
+exports.wdouble = mod_ctio.wdouble;
--- /dev/null
+'\" te
+.\" Copyright (c) 2011, Robert Mustacchi. All Rights Reserved.
+.\" Copyright (c) 2011, Joyent, Inc. 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.
+.TH CTIO 3CTYPE "December 12, 2011"
+.SH NAME
+ctio, ruint8, ruint16, ruint32, ruint64, wuint8, wuint16, wuint32, wuint64,
+rsint8, rsint16, rsint32, rsint64, wsint8, wsint16, wsint32, wsint64, rfloat,
+rdouble, wfloat, wdouble \- integer and float operations
+.SH SYNOPSIS
+.LP
+.nf
+var mod_ctype = require('ctype');
+
+\fBNumber\fR \fBmod_ctype.ruint8\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.ruint16\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.ruint32\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber[2]\fR \fBmod_ctype.ruint64\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.rsint8\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.rsint16\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.rsint32\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber[2]\fR \fBmod_ctype.rsint64\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.rfloat\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBNumber\fR \fBmod_ctype.rdouble\fR(\fBBuffer\fR \fIbuf\fR, \fBString\fR \fIendian\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wuint8\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wuint16\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wuint32\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wuint64\fR(\fBNumber[2]\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wsint8\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wsint16\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wsint32\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wsint64\fR(\fBNumber[2]\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wfloat\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.LP
+.nf
+\fBvoid\fR \fBmod_ctype.wdouble\fR(\fBNumber\fR value, \fBString\fR \fIendian\fR, \fBBuffer\fR \fIbuf\fR, \fBNumber\fR \fIoffset\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The argument \fIbuf\fR refers to a valid buffer (from calling new Buffer()). The
+argument \fIendian\fR is either the string 'big' or 'little' and controls
+whether the data in the buffer is interpreted as big or little endian. The argument
+\fIoffset\fR indicates the starting index into the buffer to read or write. All
+functions ensure that starting at \fIoffset\fR does not overflow the end of the
+buffer. The argument \fIvalue\fR is a Number that is the valid type for the
+specific function. All functions that take \fIvalue\fR as an argument, verify
+that the passed value is valid.
+
+.SS "\fBruint8()\fR, \fBruint16()\fR, \fBruint32()\fR"
+.sp
+.LP
+The \fBruint8()\fR, \fBruint16()\fR, and \fBruint32()\fR functions read an 8,
+16, and 32-bit unsigned value from \fIbuf\fR and return it. The value read is
+influenced by the values of \fIoffset\fR and \fRendian\fI.
+
+
+.SS "\fBrsint8()\fR, \fBrsint16()\fR, \fBrsint32()\fR"
+.sp
+.LP
+The \fBruint8()\fR, \fBruint16()\fR, and \fBruint32()\fR functions work just as
+\fBruint8()\fR, \fBruint16()\fR, and \fBruint32()\fR, except they return signed
+integers.
+
+.SS "\fBruint64()\fR, \fBrsint64()\fR"
+.sp
+.LP
+The \fBruint64()\fR and \fBrsint64()\fR functions read unsigned and signed 64
+bit integers respectively from \fBbuf\fR. Due to the limitations of ECMAScript's
+\fBNumber\fR type, they cannot be stored as one value without a loss of
+precision. Instead of returning the values as a single \fBNumber\fR, the
+functions return an array of two numbers. The first entry always contains the
+upper 32-bits and the second value contains the lower 32-bits. The lossy
+transformation into a number would be \fIres[0]*Math.pow(2,32)+res[1]\fR.
+Note that, unless an entry is zero, both array entries are guaranteed to have
+the same sign.
+
+.SS "\fBwuint8()\fR, \fBwuint16()\fR, \fBwuint32()\fR"
+.sp
+.LP
+The functions \fBwuint8()\fR, \fBwuint16()\fR, and \fBwuint32()\fR modify the
+contents of \fBbuf\fR by writing an 8, 16, and 32-bit unsigned integer
+respectively to \fBbuf\fR. It is illegal to pass a number that is not an integer
+within the domain of the integer size, for example, for \fBwuint8()\fR the valid
+range is \fB[0, 255]\fR. The value will be written in either big or little
+endian format based upon the value of \fBendian\fR.
+
+
+.SS "\fBwsint8()\fR, \fBwsint16()\fR, \fBwsint32()\fR"
+.sp
+.LP
+The functions \fBwsint8()\fR, \fBwsint16()\fR, and \fBwsint32()\fR function
+identically to the functions \fBwuint8()\fR, \fBwuint16()\fR, and
+\fBwuint32()\fR except that they the valid domain for \fBvalue\fR is that of a
+signed number instead of an unsigned number. For example the \fBwsint8()\fR has
+a domain of \fB[-128, 127]\fR.
+
+.SS "\fBwuint64()\fR, \fBwsint64()\fR"
+.sp
+.LP
+The functions \fBwuint64()\fR and \fBswint64()\fR write out 64-bit unsigned and
+signed integers to \fBbuf\fR. The \fBvalue\fR argument must be in the same
+format as described in \fBruint64()\fR and \fBrsint64()\fR.
+
+.SS "\fBrfloat()\fR, \fBrdouble()\fR"
+.sp
+.LP
+The functions "\fBrfloat()\fR and \fBrdouble()\fR" work like the other read
+functions, except that they read a single precision and double precision
+IEEE-754 floating point value instead.
+
+.SS "\fBwfloat()\fR, \fBwdouble()\fR"
+.sp
+.LP
+The functions "\fBrfloat()\fR and \fBrdouble()\fR" work like the other write
+functions, except that the domain for a float is that of a single precision 4
+byte value. The domain for a double is any \fBNumber\fR in ECMAScript, which is
+defined to be represented by a double.
+
+.SH ATTRIBUTES
+.sp
+.LP
+See \fBattributes\fR(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Interface Stability Committed
+_
+MT-Level See below.
+_
+Standard Not standardized.
+.TE
+
+.sp
+.LP
+
+All functions are MT-safe in so far as there aren't shared memory MT concerns in
+most node programs. If one where to concoct such an environment, these functions
+wouldn't be MT-safe.
+
+.SH SEE ALSO
+.sp
+.LP
--- /dev/null
+{
+ "name": "ctype",
+ "version": "0.5.3",
+ "description": "read and write binary structures and data types",
+ "homepage": "https://github.com/rmustacc/node-ctype",
+ "author": {
+ "name": "Robert Mustacchi",
+ "email": "rm@fingolfin.org"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "main": "ctype.js",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/rmustacc/node-ctype.git"
+ },
+ "_id": "ctype@0.5.3",
+ "dist": {
+ "shasum": "82c18c2461f74114ef16c135224ad0b9144ca12f",
+ "tarball": "http://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz"
+ },
+ "_npmVersion": "1.1.59",
+ "_npmUser": {
+ "name": "rm",
+ "email": "rm@fingolfin.org"
+ },
+ "maintainers": [
+ {
+ "name": "rm",
+ "email": "rm@fingolfin.org"
+ }
+ ],
+ "directories": {},
+ "_shasum": "82c18c2461f74114ef16c135224ad0b9144ca12f",
+ "_resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz",
+ "_from": "ctype@0.5.3"
+}
--- /dev/null
+#
+# Configuration File for JavaScript Lint 0.3.0
+# Developed by Matthias Miller (http://www.JavaScriptLint.com)
+#
+# This configuration file can be used to lint a collection of scripts, or to enable
+# or disable warnings for scripts that are linted via the command line.
+#
+
+### Warnings
+# Enable or disable warnings based on requirements.
+# Use "+WarningName" to display or "-WarningName" to suppress.
+#
++no_return_value # function {0} does not always return a value
++duplicate_formal # duplicate formal argument {0}
++equal_as_assign # test for equality (==) mistyped as assignment (=)?{0}
++var_hides_arg # variable {0} hides argument
++redeclared_var # redeclaration of {0} {1}
++anon_no_return_value # anonymous function does not always return a value
++missing_semicolon # missing semicolon
++meaningless_block # meaningless block; curly braces have no impact
++comma_separated_stmts # multiple statements separated by commas (use semicolons?)
++unreachable_code # unreachable code
++missing_break # missing break statement
++missing_break_for_last_case # missing break statement for last case in switch
++comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
+-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
++useless_void # use of the void type may be unnecessary (void is always undefined)
+-useless_quotes # quotation marks are unnecessary
++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
++use_of_label # use of label
+-block_without_braces # block statement without curly braces
++leading_decimal_point # leading decimal point may indicate a number or an object member
++trailing_decimal_point # trailing decimal point may indicate a number or an object member
+-octal_number # leading zeros make an octal number
++nested_comment # nested comment
++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
++empty_statement # empty statement or extra semicolon
+-missing_option_explicit # the "option explicit" control comment is missing
++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
++dup_option_explicit # duplicate "option explicit" control comment
++useless_assign # useless assignment
++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent)
++missing_default_case # missing default case in switch statement
++duplicate_case_in_switch # duplicate case in switch statements
++default_not_at_end # the default case is not at the end of the switch statement
++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
++useless_comparison # useless comparison; comparing identical expressions
++with_statement # with statement hides undeclared variables; use temporary variable instead
++trailing_comma_in_array # extra comma is not recommended in array initializers
++assign_to_function_call # assignment to a function call
++parseint_missing_radix # parseInt missing radix parameter
+-unreferenced_argument # argument declared but never referenced: {name}
+
+### Output format
+# Customize the format of the error message.
+# __FILE__ indicates current file path
+# __FILENAME__ indicates current file name
+# __LINE__ indicates current line
+# __ERROR__ indicates error message
+#
+# Visual Studio syntax (default):
++output-format __FILE__(__LINE__): __ERROR__
+# Alternative syntax:
+#+output-format __FILE__:__LINE__: __ERROR__
+
+
+### Context
+# Show the in-line position of the error.
+# Use "+context" to display or "-context" to suppress.
+#
++context
+
+
+### Semicolons
+# By default, assignments of an anonymous function to a variable or
+# property (such as a function prototype) must be followed by a semicolon.
+#
+#+lambda_assign_requires_semicolon # deprecated setting
+
+
+### Control Comments
+# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
+# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
+# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
+# although legacy control comments are enabled by default for backward compatibility.
+#
++legacy_control_comments
+
+
+### JScript Function Extensions
+# JScript allows member functions to be defined like this:
+# function MyObj() { /*constructor*/ }
+# function MyObj.prototype.go() { /*member function*/ }
+#
+# It also allows events to be attached like this:
+# function window::onload() { /*init page*/ }
+#
+# This is a Microsoft-only JavaScript extension. Enable this setting to allow them.
+#
+#-jscript_function_extensions # deprecated setting
+
+
+### Defining identifiers
+# By default, "option explicit" is enabled on a per-file basis.
+# To enable this for all files, use "+always_use_option_explicit"
+-always_use_option_explicit
+
+# Define certain identifiers of which the lint is not aware.
+# (Use this in conjunction with the "undeclared identifier" warning.)
+#
+# Common uses for webpages might be:
+#+define window
+#+define document
++define require
++define exports
++define console
++define Buffer
++define JSON
+
+### Files
+# Specify which files to lint
+# Use "+recurse" to enable recursion (disabled by default).
+# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
+# or "+process Folder\Path\*.htm".
+#
+#+process jsl-test.js
--- /dev/null
+#!/usr/bin/env perl
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2011 Joyent, Inc. All rights reserved.
+#
+# jsstyle - check for some common stylistic errors.
+#
+# jsstyle is a sort of "lint" for Javascript coding style. This tool is
+# derived from the cstyle tool, used to check for the style used in the
+# Solaris kernel, sometimes known as "Bill Joy Normal Form".
+#
+# There's a lot this can't check for, like proper indentation of code
+# blocks. There's also a lot more this could check for.
+#
+# A note to the non perl literate:
+#
+# perl regular expressions are pretty much like egrep
+# regular expressions, with the following special symbols
+#
+# \s any space character
+# \S any non-space character
+# \w any "word" character [a-zA-Z0-9_]
+# \W any non-word character
+# \d a digit [0-9]
+# \D a non-digit
+# \b word boundary (between \w and \W)
+# \B non-word boundary
+#
+
+require 5.0;
+use IO::File;
+use Getopt::Std;
+use strict;
+
+my $usage =
+"usage: jsstyle [-chvC] [-o constructs] file ...
+ -c check continuation indentation inside functions
+ -h perform heuristic checks that are sometimes wrong
+ -v verbose
+ -C don't check anything in header block comments
+ -o constructs
+ allow a comma-seperated list of optional constructs:
+ doxygen allow doxygen-style block comments (/** /*!)
+ splint allow splint-style lint comments (/*@ ... @*/)
+";
+
+my %opts;
+
+if (!getopts("cho:vC", \%opts)) {
+ print $usage;
+ exit 2;
+}
+
+my $check_continuation = $opts{'c'};
+my $heuristic = $opts{'h'};
+my $verbose = $opts{'v'};
+my $ignore_hdr_comment = $opts{'C'};
+
+my $doxygen_comments = 0;
+my $splint_comments = 0;
+
+if (defined($opts{'o'})) {
+ for my $x (split /,/, $opts{'o'}) {
+ if ($x eq "doxygen") {
+ $doxygen_comments = 1;
+ } elsif ($x eq "splint") {
+ $splint_comments = 1;
+ } else {
+ print "jsstyle: unrecognized construct \"$x\"\n";
+ print $usage;
+ exit 2;
+ }
+ }
+}
+
+my ($filename, $line, $prev); # shared globals
+
+my $fmt;
+my $hdr_comment_start;
+
+if ($verbose) {
+ $fmt = "%s: %d: %s\n%s\n";
+} else {
+ $fmt = "%s: %d: %s\n";
+}
+
+if ($doxygen_comments) {
+ # doxygen comments look like "/*!" or "/**"; allow them.
+ $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/;
+} else {
+ $hdr_comment_start = qr/^\s*\/\*$/;
+}
+
+# Note, following must be in single quotes so that \s and \w work right.
+my $lint_re = qr/\/\*(?:
+ jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*|
+ CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY|
+ FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*|
+ PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*?
+ )\*\//x;
+
+my $splint_re = qr/\/\*@.*?@\*\//x;
+
+my $err_stat = 0; # exit status
+
+if ($#ARGV >= 0) {
+ foreach my $arg (@ARGV) {
+ my $fh = new IO::File $arg, "r";
+ if (!defined($fh)) {
+ printf "%s: cannot open\n", $arg;
+ } else {
+ &jsstyle($arg, $fh);
+ close $fh;
+ }
+ }
+} else {
+ &jsstyle("<stdin>", *STDIN);
+}
+exit $err_stat;
+
+my $no_errs = 0; # set for JSSTYLED-protected lines
+
+sub err($) {
+ my ($error) = @_;
+ unless ($no_errs) {
+ printf $fmt, $filename, $., $error, $line;
+ $err_stat = 1;
+ }
+}
+
+sub err_prefix($$) {
+ my ($prevline, $error) = @_;
+ my $out = $prevline."\n".$line;
+ unless ($no_errs) {
+ printf $fmt, $filename, $., $error, $out;
+ $err_stat = 1;
+ }
+}
+
+sub err_prev($) {
+ my ($error) = @_;
+ unless ($no_errs) {
+ printf $fmt, $filename, $. - 1, $error, $prev;
+ $err_stat = 1;
+ }
+}
+
+sub jsstyle($$) {
+
+my ($fn, $filehandle) = @_;
+$filename = $fn; # share it globally
+
+my $in_cpp = 0;
+my $next_in_cpp = 0;
+
+my $in_comment = 0;
+my $in_header_comment = 0;
+my $comment_done = 0;
+my $in_function = 0;
+my $in_function_header = 0;
+my $in_declaration = 0;
+my $note_level = 0;
+my $nextok = 0;
+my $nocheck = 0;
+
+my $in_string = 0;
+
+my ($okmsg, $comment_prefix);
+
+$line = '';
+$prev = '';
+reset_indent();
+
+line: while (<$filehandle>) {
+ s/\r?\n$//; # strip return and newline
+
+ # save the original line, then remove all text from within
+ # double or single quotes, we do not want to check such text.
+
+ $line = $_;
+
+ #
+ # C allows strings to be continued with a backslash at the end of
+ # the line. We translate that into a quoted string on the previous
+ # line followed by an initial quote on the next line.
+ #
+ # (we assume that no-one will use backslash-continuation with character
+ # constants)
+ #
+ $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment);
+
+ #
+ # normal strings and characters
+ #
+ s/'([^\\']|\\.)*'/\'\'/g;
+ s/"([^\\"]|\\.)*"/\"\"/g;
+
+ #
+ # detect string continuation
+ #
+ if ($nocheck || $in_comment) {
+ $in_string = 0;
+ } else {
+ #
+ # Now that all full strings are replaced with "", we check
+ # for unfinished strings continuing onto the next line.
+ #
+ $in_string =
+ (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ ||
+ s/^("")*"([^\\"]|\\.)*\\$/""/);
+ }
+
+ #
+ # figure out if we are in a cpp directive
+ #
+ $in_cpp = $next_in_cpp || /^\s*#/; # continued or started
+ $next_in_cpp = $in_cpp && /\\$/; # only if continued
+
+ # strip off trailing backslashes, which appear in long macros
+ s/\s*\\$//;
+
+ # an /* END JSSTYLED */ comment ends a no-check block.
+ if ($nocheck) {
+ if (/\/\* *END *JSSTYLED *\*\//) {
+ $nocheck = 0;
+ } else {
+ reset_indent();
+ next line;
+ }
+ }
+
+ # a /*JSSTYLED*/ comment indicates that the next line is ok.
+ if ($nextok) {
+ if ($okmsg) {
+ err($okmsg);
+ }
+ $nextok = 0;
+ $okmsg = 0;
+ if (/\/\* *JSSTYLED.*\*\//) {
+ /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/;
+ $okmsg = $1;
+ $nextok = 1;
+ }
+ $no_errs = 1;
+ } elsif ($no_errs) {
+ $no_errs = 0;
+ }
+
+ # check length of line.
+ # first, a quick check to see if there is any chance of being too long.
+ if (($line =~ tr/\t/\t/) * 7 + length($line) > 80) {
+ # yes, there is a chance.
+ # replace tabs with spaces and check again.
+ my $eline = $line;
+ 1 while $eline =~
+ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
+ if (length($eline) > 80) {
+ err("line > 80 characters");
+ }
+ }
+
+ # ignore NOTE(...) annotations (assumes NOTE is on lines by itself).
+ if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE
+ s/[^()]//g; # eliminate all non-parens
+ $note_level += s/\(//g - length; # update paren nest level
+ next;
+ }
+
+ # a /* BEGIN JSSTYLED */ comment starts a no-check block.
+ if (/\/\* *BEGIN *JSSTYLED *\*\//) {
+ $nocheck = 1;
+ }
+
+ # a /*JSSTYLED*/ comment indicates that the next line is ok.
+ if (/\/\* *JSSTYLED.*\*\//) {
+ /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/;
+ $okmsg = $1;
+ $nextok = 1;
+ }
+ if (/\/\/ *JSSTYLED/) {
+ /^.*\/\/ *JSSTYLED *(.*)$/;
+ $okmsg = $1;
+ $nextok = 1;
+ }
+
+ # universal checks; apply to everything
+ if (/\t +\t/) {
+ err("spaces between tabs");
+ }
+ if (/ \t+ /) {
+ err("tabs between spaces");
+ }
+ if (/\s$/) {
+ err("space or tab at end of line");
+ }
+ if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) {
+ err("comment preceded by non-blank");
+ }
+
+ # is this the beginning or ending of a function?
+ # (not if "struct foo\n{\n")
+ if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) {
+ $in_function = 1;
+ $in_declaration = 1;
+ $in_function_header = 0;
+ $prev = $line;
+ next line;
+ }
+ if (/^}\s*(\/\*.*\*\/\s*)*$/) {
+ if ($prev =~ /^\s*return\s*;/) {
+ err_prev("unneeded return at end of function");
+ }
+ $in_function = 0;
+ reset_indent(); # we don't check between functions
+ $prev = $line;
+ next line;
+ }
+ if (/^\w*\($/) {
+ $in_function_header = 1;
+ }
+
+ # a blank line terminates the declarations within a function.
+ # XXX - but still a problem in sub-blocks.
+ if ($in_declaration && /^$/) {
+ $in_declaration = 0;
+ }
+
+ if ($comment_done) {
+ $in_comment = 0;
+ $in_header_comment = 0;
+ $comment_done = 0;
+ }
+ # does this looks like the start of a block comment?
+ if (/$hdr_comment_start/) {
+ if (!/^\t*\/\*/) {
+ err("block comment not indented by tabs");
+ }
+ $in_comment = 1;
+ /^(\s*)\//;
+ $comment_prefix = $1;
+ if ($comment_prefix eq "") {
+ $in_header_comment = 1;
+ }
+ $prev = $line;
+ next line;
+ }
+ # are we still in the block comment?
+ if ($in_comment) {
+ if (/^$comment_prefix \*\/$/) {
+ $comment_done = 1;
+ } elsif (/\*\//) {
+ $comment_done = 1;
+ err("improper block comment close")
+ unless ($ignore_hdr_comment && $in_header_comment);
+ } elsif (!/^$comment_prefix \*[ \t]/ &&
+ !/^$comment_prefix \*$/) {
+ err("improper block comment")
+ unless ($ignore_hdr_comment && $in_header_comment);
+ }
+ }
+
+ if ($in_header_comment && $ignore_hdr_comment) {
+ $prev = $line;
+ next line;
+ }
+
+ # check for errors that might occur in comments and in code.
+
+ # allow spaces to be used to draw pictures in header comments.
+ #if (/[^ ] / && !/".* .*"/ && !$in_header_comment) {
+ # err("spaces instead of tabs");
+ #}
+ #if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ &&
+ # (!/^ \w/ || $in_function != 0)) {
+ # err("indent by spaces instead of tabs");
+ #}
+ if (/^ {2,}/ && !/^ [^ ]/) {
+ err("indent by spaces instead of tabs");
+ }
+ if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) {
+ err("continuation line not indented by 4 spaces");
+ }
+
+ if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) {
+ err("improper first line of block comment");
+ }
+
+ if ($in_comment) { # still in comment, don't do further checks
+ $prev = $line;
+ next line;
+ }
+
+ if ((/[^(]\/\*\S/ || /^\/\*\S/) &&
+ !(/$lint_re/ || ($splint_comments && /$splint_re/))) {
+ err("missing blank after open comment");
+ }
+ if (/\S\*\/[^)]|\S\*\/$/ &&
+ !(/$lint_re/ || ($splint_comments && /$splint_re/))) {
+ err("missing blank before close comment");
+ }
+ if (/\/\/\S/) { # C++ comments
+ err("missing blank after start comment");
+ }
+ # check for unterminated single line comments, but allow them when
+ # they are used to comment out the argument list of a function
+ # declaration.
+ if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) {
+ err("unterminated single line comment");
+ }
+
+ if (/^(#else|#endif|#include)(.*)$/) {
+ $prev = $line;
+ next line;
+ }
+
+ #
+ # delete any comments and check everything else. Note that
+ # ".*?" is a non-greedy match, so that we don't get confused by
+ # multiple comments on the same line.
+ #
+ s/\/\*.*?\*\//\ 1/g;
+ s/\/\/.*$/\ 1/; # C++ comments
+
+ # delete any trailing whitespace; we have already checked for that.
+ s/\s*$//;
+
+ # following checks do not apply to text in comments.
+ if (/"/) {
+ err("literal string using double-quote instead of single");
+ }
+
+ if (/[^=!<>\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ ||
+ (/[^->]>[^,=>\s]/ && !/[^->]>$/) ||
+ (/[^<]<[^,=<\s]/ && !/[^<]<$/) ||
+ /[^<\s]<[^<]/ || /[^->\s]>[^>]/) {
+ err("missing space around relational operator");
+ }
+ if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ ||
+ (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) ||
+ (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) {
+ # XXX - should only check this for C++ code
+ # XXX - there are probably other forms that should be allowed
+ if (!/\soperator=/) {
+ err("missing space around assignment operator");
+ }
+ }
+ if (/[,;]\S/ && !/\bfor \(;;\)/) {
+ err("comma or semicolon followed by non-blank");
+ }
+ # allow "for" statements to have empty "while" clauses
+ if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) {
+ err("comma or semicolon preceded by blank");
+ }
+ if (/^\s*(&&|\|\|)/) {
+ err("improper boolean continuation");
+ }
+ if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) {
+ err("more than one space around boolean operator");
+ }
+ if (/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\(/) {
+ err("missing space between keyword and paren");
+ }
+ if (/(\b(catch|for|if|with|while|switch|return)\b.*){2,}/) {
+ # multiple "case" and "sizeof" allowed
+ err("more than one keyword on line");
+ }
+ if (/\b(delete|typeof|instanceOf|with|throw|catch|new|function|in|for|if|while|switch|return|case)\s\s+\(/ &&
+ !/^#if\s+\(/) {
+ err("extra space between keyword and paren");
+ }
+ # try to detect "func (x)" but not "if (x)" or
+ # "#define foo (x)" or "int (*func)();"
+ if (/\w\s\(/) {
+ my $s = $_;
+ # strip off all keywords on the line
+ s/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\s\(/XXX(/g;
+ s/#elif\s\(/XXX(/g;
+ s/^#define\s+\w+\s+\(/XXX(/;
+ # do not match things like "void (*f)();"
+ # or "typedef void (func_t)();"
+ s/\w\s\(+\*/XXX(*/g;
+ s/\b(void)\s+\(+/XXX(/og;
+ if (/\w\s\(/) {
+ err("extra space between function name and left paren");
+ }
+ $_ = $s;
+ }
+
+ if (/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) {
+ err("unparenthesized return expression");
+ }
+ if (/\btypeof\b/ && !/\btypeof\s*\(.*\)/) {
+ err("unparenthesized typeof expression");
+ }
+ if (/\(\s/) {
+ err("whitespace after left paren");
+ }
+ # allow "for" statements to have empty "continue" clauses
+ if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) {
+ err("whitespace before right paren");
+ }
+ if (/^\s*\(void\)[^ ]/) {
+ err("missing space after (void) cast");
+ }
+ if (/\S{/ && !/({|\(){/) {
+ err("missing space before left brace");
+ }
+ if ($in_function && /^\s+{/ &&
+ ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) {
+ err("left brace starting a line");
+ }
+ if (/}(else|while)/) {
+ err("missing space after right brace");
+ }
+ if (/}\s\s+(else|while)/) {
+ err("extra space after right brace");
+ }
+ if (/^\s+#/) {
+ err("preprocessor statement not in column 1");
+ }
+ if (/^#\s/) {
+ err("blank after preprocessor #");
+ }
+
+ #
+ # We completely ignore, for purposes of indentation:
+ # * lines outside of functions
+ # * preprocessor lines
+ #
+ if ($check_continuation && $in_function && !$in_cpp) {
+ process_indent($_);
+ }
+
+ if ($heuristic) {
+ # cannot check this everywhere due to "struct {\n...\n} foo;"
+ if ($in_function && !$in_declaration &&
+ /}./ && !/}\s+=/ && !/{.*}[;,]$/ && !/}(\s|\ 1)*$/ &&
+ !/} (else|while)/ && !/}}/) {
+ err("possible bad text following right brace");
+ }
+ # cannot check this because sub-blocks in
+ # the middle of code are ok
+ if ($in_function && /^\s+{/) {
+ err("possible left brace starting a line");
+ }
+ }
+ if (/^\s*else\W/) {
+ if ($prev =~ /^\s*}$/) {
+ err_prefix($prev,
+ "else and right brace should be on same line");
+ }
+ }
+ $prev = $line;
+}
+
+if ($prev eq "") {
+ err("last line in file is blank");
+}
+
+}
+
+#
+# Continuation-line checking
+#
+# The rest of this file contains the code for the continuation checking
+# engine. It's a pretty simple state machine which tracks the expression
+# depth (unmatched '('s and '['s).
+#
+# Keep in mind that the argument to process_indent() has already been heavily
+# processed; all comments have been replaced by control-A, and the contents of
+# strings and character constants have been elided.
+#
+
+my $cont_in; # currently inside of a continuation
+my $cont_off; # skipping an initializer or definition
+my $cont_noerr; # suppress cascading errors
+my $cont_start; # the line being continued
+my $cont_base; # the base indentation
+my $cont_first; # this is the first line of a statement
+my $cont_multiseg; # this continuation has multiple segments
+
+my $cont_special; # this is a C statement (if, for, etc.)
+my $cont_macro; # this is a macro
+my $cont_case; # this is a multi-line case
+
+my @cont_paren; # the stack of unmatched ( and [s we've seen
+
+sub
+reset_indent()
+{
+ $cont_in = 0;
+ $cont_off = 0;
+}
+
+sub
+delabel($)
+{
+ #
+ # replace labels with tabs. Note that there may be multiple
+ # labels on a line.
+ #
+ local $_ = $_[0];
+
+ while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) {
+ my ($pre_tabs, $label, $rest) = ($1, $2, $3);
+ $_ = $pre_tabs;
+ while ($label =~ s/^([^\t]*)(\t+)//) {
+ $_ .= "\t" x (length($2) + length($1) / 8);
+ }
+ $_ .= ("\t" x (length($label) / 8)).$rest;
+ }
+
+ return ($_);
+}
+
+sub
+process_indent($)
+{
+ require strict;
+ local $_ = $_[0]; # preserve the global $_
+
+ s/\ 1//g; # No comments
+ s/\s+$//; # Strip trailing whitespace
+
+ return if (/^$/); # skip empty lines
+
+ # regexps used below; keywords taking (), macros, and continued cases
+ my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b';
+ my $macro = '[A-Z_][A-Z_0-9]*\(';
+ my $case = 'case\b[^:]*$';
+
+ # skip over enumerations, array definitions, initializers, etc.
+ if ($cont_off <= 0 && !/^\s*$special/ &&
+ (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ ||
+ (/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) {
+ $cont_in = 0;
+ $cont_off = tr/{/{/ - tr/}/}/;
+ return;
+ }
+ if ($cont_off) {
+ $cont_off += tr/{/{/ - tr/}/}/;
+ return;
+ }
+
+ if (!$cont_in) {
+ $cont_start = $line;
+
+ if (/^\t* /) {
+ err("non-continuation indented 4 spaces");
+ $cont_noerr = 1; # stop reporting
+ }
+ $_ = delabel($_); # replace labels with tabs
+
+ # check if the statement is complete
+ return if (/^\s*\}?$/);
+ return if (/^\s*\}?\s*else\s*\{?$/);
+ return if (/^\s*do\s*\{?$/);
+ return if (/{$/);
+ return if (/}[,;]?$/);
+
+ # Allow macros on their own lines
+ return if (/^\s*[A-Z_][A-Z_0-9]*$/);
+
+ # cases we don't deal with, generally non-kosher
+ if (/{/) {
+ err("stuff after {");
+ return;
+ }
+
+ # Get the base line, and set up the state machine
+ /^(\t*)/;
+ $cont_base = $1;
+ $cont_in = 1;
+ @cont_paren = ();
+ $cont_first = 1;
+ $cont_multiseg = 0;
+
+ # certain things need special processing
+ $cont_special = /^\s*$special/? 1 : 0;
+ $cont_macro = /^\s*$macro/? 1 : 0;
+ $cont_case = /^\s*$case/? 1 : 0;
+ } else {
+ $cont_first = 0;
+
+ # Strings may be pulled back to an earlier (half-)tabstop
+ unless ($cont_noerr || /^$cont_base / ||
+ (/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) {
+ err_prefix($cont_start,
+ "continuation should be indented 4 spaces");
+ }
+ }
+
+ my $rest = $_; # keeps the remainder of the line
+
+ #
+ # The split matches 0 characters, so that each 'special' character
+ # is processed separately. Parens and brackets are pushed and
+ # popped off the @cont_paren stack. For normal processing, we wait
+ # until a ; or { terminates the statement. "special" processing
+ # (if/for/while/switch) is allowed to stop when the stack empties,
+ # as is macro processing. Case statements are terminated with a :
+ # and an empty paren stack.
+ #
+ foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) {
+ next if (length($_) == 0);
+
+ # rest contains the remainder of the line
+ my $rxp = "[^\Q$_\E]*\Q$_\E";
+ $rest =~ s/^$rxp//;
+
+ if (/\(/ || /\[/) {
+ push @cont_paren, $_;
+ } elsif (/\)/ || /\]/) {
+ my $cur = $_;
+ tr/\)\]/\(\[/;
+
+ my $old = (pop @cont_paren);
+ if (!defined($old)) {
+ err("unexpected '$cur'");
+ $cont_in = 0;
+ last;
+ } elsif ($old ne $_) {
+ err("'$cur' mismatched with '$old'");
+ $cont_in = 0;
+ last;
+ }
+
+ #
+ # If the stack is now empty, do special processing
+ # for if/for/while/switch and macro statements.
+ #
+ next if (@cont_paren != 0);
+ if ($cont_special) {
+ if ($rest =~ /^\s*{?$/) {
+ $cont_in = 0;
+ last;
+ }
+ if ($rest =~ /^\s*;$/) {
+ err("empty if/for/while body ".
+ "not on its own line");
+ $cont_in = 0;
+ last;
+ }
+ if (!$cont_first && $cont_multiseg == 1) {
+ err_prefix($cont_start,
+ "multiple statements continued ".
+ "over multiple lines");
+ $cont_multiseg = 2;
+ } elsif ($cont_multiseg == 0) {
+ $cont_multiseg = 1;
+ }
+ # We've finished this section, start
+ # processing the next.
+ goto section_ended;
+ }
+ if ($cont_macro) {
+ if ($rest =~ /^$/) {
+ $cont_in = 0;
+ last;
+ }
+ }
+ } elsif (/\;/) {
+ if ($cont_case) {
+ err("unexpected ;");
+ } elsif (!$cont_special) {
+ err("unexpected ;") if (@cont_paren != 0);
+ if (!$cont_first && $cont_multiseg == 1) {
+ err_prefix($cont_start,
+ "multiple statements continued ".
+ "over multiple lines");
+ $cont_multiseg = 2;
+ } elsif ($cont_multiseg == 0) {
+ $cont_multiseg = 1;
+ }
+ if ($rest =~ /^$/) {
+ $cont_in = 0;
+ last;
+ }
+ if ($rest =~ /^\s*special/) {
+ err("if/for/while/switch not started ".
+ "on its own line");
+ }
+ goto section_ended;
+ }
+ } elsif (/\{/) {
+ err("{ while in parens/brackets") if (@cont_paren != 0);
+ err("stuff after {") if ($rest =~ /[^\s}]/);
+ $cont_in = 0;
+ last;
+ } elsif (/\}/) {
+ err("} while in parens/brackets") if (@cont_paren != 0);
+ if (!$cont_special && $rest !~ /^\s*(while|else)\b/) {
+ if ($rest =~ /^$/) {
+ err("unexpected }");
+ } else {
+ err("stuff after }");
+ }
+ $cont_in = 0;
+ last;
+ }
+ } elsif (/\:/ && $cont_case && @cont_paren == 0) {
+ err("stuff after multi-line case") if ($rest !~ /$^/);
+ $cont_in = 0;
+ last;
+ }
+ next;
+section_ended:
+ # End of a statement or if/while/for loop. Reset
+ # cont_special and cont_macro based on the rest of the
+ # line.
+ $cont_special = ($rest =~ /^\s*$special/)? 1 : 0;
+ $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0;
+ $cont_case = 0;
+ next;
+ }
+ $cont_noerr = 0 if (!$cont_in);
+}
--- /dev/null
+{
+ "name": "http-signature",
+ "description": "Reference implementation of Joyent's HTTP Signature scheme.",
+ "version": "0.10.1",
+ "license": "MIT",
+ "author": {
+ "name": "Joyent, Inc"
+ },
+ "contributors": [
+ {
+ "name": "Mark Cavage",
+ "email": "mcavage@gmail.com"
+ },
+ {
+ "name": "David I. Lehn",
+ "email": "dil@lehn.org"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/joyent/node-http-signature.git"
+ },
+ "homepage": "https://github.com/joyent/node-http-signature/",
+ "bugs": {
+ "url": "https://github.com/joyent/node-http-signature/issues"
+ },
+ "keywords": [
+ "https",
+ "request"
+ ],
+ "engines": {
+ "node": ">=0.8"
+ },
+ "main": "lib/index.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "assert-plus": "^0.1.5",
+ "asn1": "0.1.11",
+ "ctype": "0.5.3"
+ },
+ "devDependencies": {
+ "node-uuid": "^1.4.1",
+ "tap": "0.4.2"
+ },
+ "_id": "http-signature@0.10.1",
+ "_shasum": "4fbdac132559aa8323121e540779c0a012b27e66",
+ "_resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz",
+ "_from": "http-signature@>=0.10.0 <0.11.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "pfmooney",
+ "email": "patrick.f.mooney@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mcavage",
+ "email": "mcavage@gmail.com"
+ },
+ {
+ "name": "pfmooney",
+ "email": "patrick.f.mooney@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "4fbdac132559aa8323121e540779c0a012b27e66",
+ "tarball": "http://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz"
+ },
+ "directories": {},
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+{
+ "predef": [ ]
+ , "bitwise": false
+ , "camelcase": false
+ , "curly": false
+ , "eqeqeq": false
+ , "forin": false
+ , "immed": false
+ , "latedef": false
+ , "noarg": true
+ , "noempty": true
+ , "nonew": true
+ , "plusplus": false
+ , "quotmark": true
+ , "regexp": false
+ , "undef": true
+ , "unused": true
+ , "strict": false
+ , "trailing": true
+ , "maxlen": 120
+ , "asi": true
+ , "boss": true
+ , "debug": true
+ , "eqnull": true
+ , "esnext": true
+ , "evil": true
+ , "expr": true
+ , "funcscope": false
+ , "globalstrict": false
+ , "iterator": false
+ , "lastsemic": true
+ , "laxbreak": true
+ , "laxcomma": true
+ , "loopfunc": true
+ , "multistr": false
+ , "onecase": false
+ , "proto": false
+ , "regexdash": false
+ , "scripturl": true
+ , "smarttabs": false
+ , "shadow": false
+ , "sub": true
+ , "supernew": false
+ , "validthis": true
+ , "browser": true
+ , "couch": false
+ , "devel": false
+ , "dojo": false
+ , "mootools": false
+ , "node": true
+ , "nonstandard": true
+ , "prototypejs": false
+ , "rhino": false
+ , "worker": true
+ , "wsh": false
+ , "nomen": false
+ , "onevar": false
+ , "passfail": false
+}
\ No newline at end of file
--- /dev/null
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
+ - "0.11"
+branches:
+ only:
+ - master
+notifications:
+ email:
+ - rod@vagg.org
+script: npm test
--- /dev/null
+The MIT License (MIT)
+=====================
+
+Copyright (c) 2015 Rod Vagg
+---------------------------
+
+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.
--- /dev/null
+# isStream
+
+[](http://travis-ci.org/rvagg/isstream)
+
+**Test if an object is a `Stream`**
+
+[](https://nodei.co/npm/isstream/)
+
+The missing `Stream.isStream(obj)`: determine if an object is standard Node.js `Stream`. Works for Node-core `Stream` objects (for 0.8, 0.10, 0.11, and in theory, older and newer versions) and all versions of **[readable-stream](https://github.com/isaacs/readable-stream)**.
+
+## Usage:
+
+```js
+var isStream = require('isstream')
+var Stream = require('stream')
+
+isStream(new Stream()) // true
+
+isStream({}) // false
+
+isStream(new Stream.Readable()) // true
+isStream(new Stream.Writable()) // true
+isStream(new Stream.Duplex()) // true
+isStream(new Stream.Transform()) // true
+isStream(new Stream.PassThrough()) // true
+```
+
+## But wait! There's more!
+
+You can also test for `isReadable(obj)`, `isWritable(obj)` and `isDuplex(obj)` to test for implementations of Streams2 (and Streams3) base classes.
+
+```js
+var isReadable = require('isstream').isReadable
+var isWritable = require('isstream').isWritable
+var isDuplex = require('isstream').isDuplex
+var Stream = require('stream')
+
+isReadable(new Stream()) // false
+isWritable(new Stream()) // false
+isDuplex(new Stream()) // false
+
+isReadable(new Stream.Readable()) // true
+isReadable(new Stream.Writable()) // false
+isReadable(new Stream.Duplex()) // true
+isReadable(new Stream.Transform()) // true
+isReadable(new Stream.PassThrough()) // true
+
+isWritable(new Stream.Readable()) // false
+isWritable(new Stream.Writable()) // true
+isWritable(new Stream.Duplex()) // true
+isWritable(new Stream.Transform()) // true
+isWritable(new Stream.PassThrough()) // true
+
+isDuplex(new Stream.Readable()) // false
+isDuplex(new Stream.Writable()) // false
+isDuplex(new Stream.Duplex()) // true
+isDuplex(new Stream.Transform()) // true
+isDuplex(new Stream.PassThrough()) // true
+```
+
+*Reminder: when implementing your own streams, please [use **readable-stream** rather than core streams](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html).*
+
+
+## License
+
+**isStream** is Copyright (c) 2015 Rod Vagg [@rvagg](https://twitter.com/rvagg) and licenced under the MIT licence. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.
--- /dev/null
+var stream = require('stream')
+
+
+function isStream (obj) {
+ return obj instanceof stream.Stream
+}
+
+
+function isReadable (obj) {
+ return isStream(obj) && typeof obj._read == 'function' && typeof obj._readableState == 'object'
+}
+
+
+function isWritable (obj) {
+ return isStream(obj) && typeof obj._write == 'function' && typeof obj._writableState == 'object'
+}
+
+
+function isDuplex (obj) {
+ return isReadable(obj) && isWritable(obj)
+}
+
+
+module.exports = isStream
+module.exports.isReadable = isReadable
+module.exports.isWritable = isWritable
+module.exports.isDuplex = isDuplex
--- /dev/null
+{
+ "name": "isstream",
+ "version": "0.1.2",
+ "description": "Determine if an object is a Stream",
+ "main": "isstream.js",
+ "scripts": {
+ "test": "tar --xform 's/^package/readable-stream-1.0/' -zxf readable-stream-1.0.*.tgz && tar --xform 's/^package/readable-stream-1.1/' -zxf readable-stream-1.1.*.tgz && node test.js; rm -rf readable-stream-1.?/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rvagg/isstream.git"
+ },
+ "keywords": [
+ "stream",
+ "type",
+ "streams",
+ "readable-stream",
+ "hippo"
+ ],
+ "devDependencies": {
+ "tape": "~2.12.3",
+ "core-util-is": "~1.0.0",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x",
+ "inherits": "~2.0.1"
+ },
+ "author": {
+ "name": "Rod Vagg",
+ "email": "rod@vagg.org"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/rvagg/isstream/issues"
+ },
+ "homepage": "https://github.com/rvagg/isstream",
+ "gitHead": "cd39cba6da939b4fc9110825203adc506422c3dc",
+ "_id": "isstream@0.1.2",
+ "_shasum": "47e63f7af55afa6f92e1500e690eb8b8529c099a",
+ "_from": "isstream@>=0.1.1 <0.2.0",
+ "_npmVersion": "2.6.1",
+ "_nodeVersion": "1.4.3",
+ "_npmUser": {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ },
+ "maintainers": [
+ {
+ "name": "rvagg",
+ "email": "rod@vagg.org"
+ }
+ ],
+ "dist": {
+ "shasum": "47e63f7af55afa6f92e1500e690eb8b8529c099a",
+ "tarball": "http://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+var tape = require('tape')
+ , EE = require('events').EventEmitter
+ , util = require('util')
+
+
+ , isStream = require('./')
+ , isReadable = require('./').isReadable
+ , isWritable = require('./').isWritable
+ , isDuplex = require('./').isDuplex
+
+ , CoreStreams = require('stream')
+ , ReadableStream10 = require('./readable-stream-1.0/')
+ , ReadableStream11 = require('./readable-stream-1.1/')
+
+
+function test (pass, type, stream) {
+ tape('isStream(' + type + ')', function (t) {
+ t.plan(1)
+ t.ok(pass === isStream(stream), type)
+ })
+}
+
+
+function testReadable (pass, type, stream) {
+ tape('isReadable(' + type + ')', function (t) {
+ t.plan(1)
+ t.ok(pass === isReadable(stream), type)
+ })
+}
+
+
+function testWritable (pass, type, stream) {
+ tape('isWritable(' + type + ')', function (t) {
+ t.plan(1)
+ t.ok(pass === isWritable(stream), type)
+ })
+}
+
+
+function testDuplex (pass, type, stream) {
+ tape('isDuplex(' + type + ')', function (t) {
+ t.plan(1)
+ t.ok(pass === isDuplex(stream), type)
+ })
+}
+
+
+[ undefined, null, '', true, false, 0, 1, 1.0, 'string', {}, function foo () {} ].forEach(function (o) {
+ test(false, 'non-stream / primitive: ' + (JSON.stringify(o) || (o && o.toString()) || o), o)
+})
+
+
+test(false, 'fake stream obj', { pipe: function () {} })
+
+
+;(function () {
+
+ // looks like a stream!
+
+ function Stream () {
+ EE.call(this)
+ }
+ util.inherits(Stream, EE)
+ Stream.prototype.pipe = function () {}
+ Stream.Stream = Stream
+
+ test(false, 'fake stream "new Stream()"', new Stream())
+
+}())
+
+
+test(true, 'CoreStreams.Stream', new (CoreStreams.Stream)())
+test(true, 'CoreStreams.Readable', new (CoreStreams.Readable)())
+test(true, 'CoreStreams.Writable', new (CoreStreams.Writable)())
+test(true, 'CoreStreams.Duplex', new (CoreStreams.Duplex)())
+test(true, 'CoreStreams.Transform', new (CoreStreams.Transform)())
+test(true, 'CoreStreams.PassThrough', new (CoreStreams.PassThrough)())
+
+test(true, 'ReadableStream10.Readable', new (ReadableStream10.Readable)())
+test(true, 'ReadableStream10.Writable', new (ReadableStream10.Writable)())
+test(true, 'ReadableStream10.Duplex', new (ReadableStream10.Duplex)())
+test(true, 'ReadableStream10.Transform', new (ReadableStream10.Transform)())
+test(true, 'ReadableStream10.PassThrough', new (ReadableStream10.PassThrough)())
+
+test(true, 'ReadableStream11.Readable', new (ReadableStream11.Readable)())
+test(true, 'ReadableStream11.Writable', new (ReadableStream11.Writable)())
+test(true, 'ReadableStream11.Duplex', new (ReadableStream11.Duplex)())
+test(true, 'ReadableStream11.Transform', new (ReadableStream11.Transform)())
+test(true, 'ReadableStream11.PassThrough', new (ReadableStream11.PassThrough)())
+
+
+testReadable(false, 'CoreStreams.Stream', new (CoreStreams.Stream)())
+testReadable(true, 'CoreStreams.Readable', new (CoreStreams.Readable)())
+testReadable(false, 'CoreStreams.Writable', new (CoreStreams.Writable)())
+testReadable(true, 'CoreStreams.Duplex', new (CoreStreams.Duplex)())
+testReadable(true, 'CoreStreams.Transform', new (CoreStreams.Transform)())
+testReadable(true, 'CoreStreams.PassThrough', new (CoreStreams.PassThrough)())
+
+testReadable(true, 'ReadableStream10.Readable', new (ReadableStream10.Readable)())
+testReadable(false, 'ReadableStream10.Writable', new (ReadableStream10.Writable)())
+testReadable(true, 'ReadableStream10.Duplex', new (ReadableStream10.Duplex)())
+testReadable(true, 'ReadableStream10.Transform', new (ReadableStream10.Transform)())
+testReadable(true, 'ReadableStream10.PassThrough', new (ReadableStream10.PassThrough)())
+
+testReadable(true, 'ReadableStream11.Readable', new (ReadableStream11.Readable)())
+testReadable(false, 'ReadableStream11.Writable', new (ReadableStream11.Writable)())
+testReadable(true, 'ReadableStream11.Duplex', new (ReadableStream11.Duplex)())
+testReadable(true, 'ReadableStream11.Transform', new (ReadableStream11.Transform)())
+testReadable(true, 'ReadableStream11.PassThrough', new (ReadableStream11.PassThrough)())
+
+
+testWritable(false, 'CoreStreams.Stream', new (CoreStreams.Stream)())
+testWritable(false, 'CoreStreams.Readable', new (CoreStreams.Readable)())
+testWritable(true, 'CoreStreams.Writable', new (CoreStreams.Writable)())
+testWritable(true, 'CoreStreams.Duplex', new (CoreStreams.Duplex)())
+testWritable(true, 'CoreStreams.Transform', new (CoreStreams.Transform)())
+testWritable(true, 'CoreStreams.PassThrough', new (CoreStreams.PassThrough)())
+
+testWritable(false, 'ReadableStream10.Readable', new (ReadableStream10.Readable)())
+testWritable(true, 'ReadableStream10.Writable', new (ReadableStream10.Writable)())
+testWritable(true, 'ReadableStream10.Duplex', new (ReadableStream10.Duplex)())
+testWritable(true, 'ReadableStream10.Transform', new (ReadableStream10.Transform)())
+testWritable(true, 'ReadableStream10.PassThrough', new (ReadableStream10.PassThrough)())
+
+testWritable(false, 'ReadableStream11.Readable', new (ReadableStream11.Readable)())
+testWritable(true, 'ReadableStream11.Writable', new (ReadableStream11.Writable)())
+testWritable(true, 'ReadableStream11.Duplex', new (ReadableStream11.Duplex)())
+testWritable(true, 'ReadableStream11.Transform', new (ReadableStream11.Transform)())
+testWritable(true, 'ReadableStream11.PassThrough', new (ReadableStream11.PassThrough)())
+
+
+testDuplex(false, 'CoreStreams.Stream', new (CoreStreams.Stream)())
+testDuplex(false, 'CoreStreams.Readable', new (CoreStreams.Readable)())
+testDuplex(false, 'CoreStreams.Writable', new (CoreStreams.Writable)())
+testDuplex(true, 'CoreStreams.Duplex', new (CoreStreams.Duplex)())
+testDuplex(true, 'CoreStreams.Transform', new (CoreStreams.Transform)())
+testDuplex(true, 'CoreStreams.PassThrough', new (CoreStreams.PassThrough)())
+
+testDuplex(false, 'ReadableStream10.Readable', new (ReadableStream10.Readable)())
+testDuplex(false, 'ReadableStream10.Writable', new (ReadableStream10.Writable)())
+testDuplex(true, 'ReadableStream10.Duplex', new (ReadableStream10.Duplex)())
+testDuplex(true, 'ReadableStream10.Transform', new (ReadableStream10.Transform)())
+testDuplex(true, 'ReadableStream10.PassThrough', new (ReadableStream10.PassThrough)())
+
+testDuplex(false, 'ReadableStream11.Readable', new (ReadableStream11.Readable)())
+testDuplex(false, 'ReadableStream11.Writable', new (ReadableStream11.Writable)())
+testDuplex(true, 'ReadableStream11.Duplex', new (ReadableStream11.Duplex)())
+testDuplex(true, 'ReadableStream11.Transform', new (ReadableStream11.Transform)())
+testDuplex(true, 'ReadableStream11.PassThrough', new (ReadableStream11.PassThrough)())
+
+
+;[ CoreStreams, ReadableStream10, ReadableStream11 ].forEach(function (p) {
+ [ 'Stream', 'Readable', 'Writable', 'Duplex', 'Transform', 'PassThrough' ].forEach(function (k) {
+ if (!p[k])
+ return
+
+ function SubStream () {
+ p[k].call(this)
+ }
+ util.inherits(SubStream, p[k])
+
+ test(true, 'Stream subclass: ' + p.name + '.' + k, new SubStream())
+
+ })
+})
+
+
+
--- /dev/null
+## Unreleased
+- Fixes stringify to only take ancestors into account when checking
+ circularity.
+ It previously assumed every visited object was circular which led to [false
+ positives][issue9].
+ Uses the tiny serializer I wrote for [Must.js][must] a year and a half ago.
+- Fixes calling the `replacer` function in the proper context (`thisArg`).
+- Fixes calling the `cycleReplacer` function in the proper context (`thisArg`).
+- Speeds serializing by a factor of
+ Big-O(h-my-god-it-linearly-searched-every-object) it had ever seen. Searching
+ only the ancestors for a circular references speeds up things considerably.
+
+[must]: https://github.com/moll/js-must
+[issue9]: https://github.com/isaacs/json-stringify-safe/issues/9
--- /dev/null
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null
+NODE_OPTS =
+TEST_OPTS =
+
+love:
+ @echo "Feel like makin' love."
+
+test:
+ @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot $(TEST_OPTS)
+
+spec:
+ @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec $(TEST_OPTS)
+
+autotest:
+ @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot --watch $(TEST_OPTS)
+
+autospec:
+ @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec --watch $(TEST_OPTS)
+
+pack:
+ @file=$$(npm pack); echo "$$file"; tar tf "$$file"
+
+publish:
+ npm publish
+
+tag:
+ git tag "v$$(node -e 'console.log(require("./package").version)')"
+
+clean:
+ rm -f *.tgz
+ npm prune --production
+
+.PHONY: love
+.PHONY: test spec autotest autospec
+.PHONY: pack publish tag
+.PHONY: clean
--- /dev/null
+# json-stringify-safe
+
+Like JSON.stringify, but doesn't throw on circular references.
+
+## Usage
+
+Takes the same arguments as `JSON.stringify`.
+
+```javascript
+var stringify = require('json-stringify-safe');
+var circularObj = {};
+circularObj.circularRef = circularObj;
+circularObj.list = [ circularObj, circularObj ];
+console.log(stringify(circularObj, null, 2));
+```
+
+Output:
+
+```json
+{
+ "circularRef": "[Circular]",
+ "list": [
+ "[Circular]",
+ "[Circular]"
+ ]
+}
+```
+
+## Details
+
+```
+stringify(obj, serializer, indent, decycler)
+```
+
+The first three arguments are the same as to JSON.stringify. The last
+is an argument that's only used when the object has been seen already.
+
+The default `decycler` function returns the string `'[Circular]'`.
+If, for example, you pass in `function(k,v){}` (return nothing) then it
+will prune cycles. If you pass in `function(k,v){ return {foo: 'bar'}}`,
+then cyclical objects will always be represented as `{"foo":"bar"}` in
+the result.
+
+```
+stringify.getSerialize(serializer, decycler)
+```
+
+Returns a serializer that can be used elsewhere. This is the actual
+function that's passed to JSON.stringify.
+
+**Note** that the function returned from `getSerialize` is stateful for now, so
+do **not** use it more than once.
--- /dev/null
+{
+ "name": "json-stringify-safe",
+ "version": "5.0.1",
+ "description": "Like JSON.stringify, but doesn't blow up on circular refs.",
+ "keywords": [
+ "json",
+ "stringify",
+ "circular",
+ "safe"
+ ],
+ "homepage": "https://github.com/isaacs/json-stringify-safe",
+ "bugs": {
+ "url": "https://github.com/isaacs/json-stringify-safe/issues"
+ },
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me"
+ },
+ "contributors": [
+ {
+ "name": "Andri Möll",
+ "email": "andri@dot.ee",
+ "url": "http://themoll.com"
+ }
+ ],
+ "license": "ISC",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/json-stringify-safe.git"
+ },
+ "main": "stringify.js",
+ "scripts": {
+ "test": "node test.js"
+ },
+ "devDependencies": {
+ "mocha": ">= 2.1.0 < 3",
+ "must": ">= 0.12 < 0.13",
+ "sinon": ">= 1.12.2 < 2"
+ },
+ "gitHead": "3890dceab3ad14f8701e38ca74f38276abc76de5",
+ "_id": "json-stringify-safe@5.0.1",
+ "_shasum": "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb",
+ "_from": "json-stringify-safe@>=5.0.0 <5.1.0",
+ "_npmVersion": "2.10.0",
+ "_nodeVersion": "2.0.1",
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "isaacs@npmjs.com"
+ },
+ "dist": {
+ "shasum": "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb",
+ "tarball": "http://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ {
+ "name": "moll",
+ "email": "andri@dot.ee"
+ }
+ ],
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+}
--- /dev/null
+exports = module.exports = stringify
+exports.getSerialize = serializer
+
+function stringify(obj, replacer, spaces, cycleReplacer) {
+ return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces)
+}
+
+function serializer(replacer, cycleReplacer) {
+ var stack = [], keys = []
+
+ if (cycleReplacer == null) cycleReplacer = function(key, value) {
+ if (stack[0] === value) return "[Circular ~]"
+ return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]"
+ }
+
+ return function(key, value) {
+ if (stack.length > 0) {
+ var thisPos = stack.indexOf(this)
+ ~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
+ ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
+ if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value)
+ }
+ else stack.push(value)
+
+ return replacer == null ? value : replacer.call(this, key, value)
+ }
+}
--- /dev/null
+--recursive
+--require must
--- /dev/null
+var Sinon = require("sinon")
+var stringify = require("..")
+function jsonify(obj) { return JSON.stringify(obj, null, 2) }
+
+describe("Stringify", function() {
+ it("must stringify circular objects", function() {
+ var obj = {name: "Alice"}
+ obj.self = obj
+ var json = stringify(obj, null, 2)
+ json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"}))
+ })
+
+ it("must stringify circular objects with intermediaries", function() {
+ var obj = {name: "Alice"}
+ obj.identity = {self: obj}
+ var json = stringify(obj, null, 2)
+ json.must.eql(jsonify({name: "Alice", identity: {self: "[Circular ~]"}}))
+ })
+
+ it("must stringify circular objects deeper", function() {
+ var obj = {name: "Alice", child: {name: "Bob"}}
+ obj.child.self = obj.child
+
+ stringify(obj, null, 2).must.eql(jsonify({
+ name: "Alice",
+ child: {name: "Bob", self: "[Circular ~.child]"}
+ }))
+ })
+
+ it("must stringify circular objects deeper with intermediaries", function() {
+ var obj = {name: "Alice", child: {name: "Bob"}}
+ obj.child.identity = {self: obj.child}
+
+ stringify(obj, null, 2).must.eql(jsonify({
+ name: "Alice",
+ child: {name: "Bob", identity: {self: "[Circular ~.child]"}}
+ }))
+ })
+
+ it("must stringify circular objects in an array", function() {
+ var obj = {name: "Alice"}
+ obj.self = [obj, obj]
+
+ stringify(obj, null, 2).must.eql(jsonify({
+ name: "Alice", self: ["[Circular ~]", "[Circular ~]"]
+ }))
+ })
+
+ it("must stringify circular objects deeper in an array", function() {
+ var obj = {name: "Alice", children: [{name: "Bob"}, {name: "Eve"}]}
+ obj.children[0].self = obj.children[0]
+ obj.children[1].self = obj.children[1]
+
+ stringify(obj, null, 2).must.eql(jsonify({
+ name: "Alice",
+ children: [
+ {name: "Bob", self: "[Circular ~.children.0]"},
+ {name: "Eve", self: "[Circular ~.children.1]"}
+ ]
+ }))
+ })
+
+ it("must stringify circular arrays", function() {
+ var obj = []
+ obj.push(obj)
+ obj.push(obj)
+ var json = stringify(obj, null, 2)
+ json.must.eql(jsonify(["[Circular ~]", "[Circular ~]"]))
+ })
+
+ it("must stringify circular arrays with intermediaries", function() {
+ var obj = []
+ obj.push({name: "Alice", self: obj})
+ obj.push({name: "Bob", self: obj})
+
+ stringify(obj, null, 2).must.eql(jsonify([
+ {name: "Alice", self: "[Circular ~]"},
+ {name: "Bob", self: "[Circular ~]"}
+ ]))
+ })
+
+ it("must stringify repeated objects in objects", function() {
+ var obj = {}
+ var alice = {name: "Alice"}
+ obj.alice1 = alice
+ obj.alice2 = alice
+
+ stringify(obj, null, 2).must.eql(jsonify({
+ alice1: {name: "Alice"},
+ alice2: {name: "Alice"}
+ }))
+ })
+
+ it("must stringify repeated objects in arrays", function() {
+ var alice = {name: "Alice"}
+ var obj = [alice, alice]
+ var json = stringify(obj, null, 2)
+ json.must.eql(jsonify([{name: "Alice"}, {name: "Alice"}]))
+ })
+
+ it("must call given decycler and use its output", function() {
+ var obj = {}
+ obj.a = obj
+ obj.b = obj
+
+ var decycle = Sinon.spy(function() { return decycle.callCount })
+ var json = stringify(obj, null, 2, decycle)
+ json.must.eql(jsonify({a: 1, b: 2}, null, 2))
+
+ decycle.callCount.must.equal(2)
+ decycle.thisValues[0].must.equal(obj)
+ decycle.args[0][0].must.equal("a")
+ decycle.args[0][1].must.equal(obj)
+ decycle.thisValues[1].must.equal(obj)
+ decycle.args[1][0].must.equal("b")
+ decycle.args[1][1].must.equal(obj)
+ })
+
+ it("must call replacer and use its output", function() {
+ var obj = {name: "Alice", child: {name: "Bob"}}
+
+ var replacer = Sinon.spy(bangString)
+ var json = stringify(obj, replacer, 2)
+ json.must.eql(jsonify({name: "Alice!", child: {name: "Bob!"}}))
+
+ replacer.callCount.must.equal(4)
+ replacer.args[0][0].must.equal("")
+ replacer.args[0][1].must.equal(obj)
+ replacer.thisValues[1].must.equal(obj)
+ replacer.args[1][0].must.equal("name")
+ replacer.args[1][1].must.equal("Alice")
+ replacer.thisValues[2].must.equal(obj)
+ replacer.args[2][0].must.equal("child")
+ replacer.args[2][1].must.equal(obj.child)
+ replacer.thisValues[3].must.equal(obj.child)
+ replacer.args[3][0].must.equal("name")
+ replacer.args[3][1].must.equal("Bob")
+ })
+
+ it("must call replacer after describing circular references", function() {
+ var obj = {name: "Alice"}
+ obj.self = obj
+
+ var replacer = Sinon.spy(bangString)
+ var json = stringify(obj, replacer, 2)
+ json.must.eql(jsonify({name: "Alice!", self: "[Circular ~]!"}))
+
+ replacer.callCount.must.equal(3)
+ replacer.args[0][0].must.equal("")
+ replacer.args[0][1].must.equal(obj)
+ replacer.thisValues[1].must.equal(obj)
+ replacer.args[1][0].must.equal("name")
+ replacer.args[1][1].must.equal("Alice")
+ replacer.thisValues[2].must.equal(obj)
+ replacer.args[2][0].must.equal("self")
+ replacer.args[2][1].must.equal("[Circular ~]")
+ })
+
+ it("must call given decycler and use its output for nested objects",
+ function() {
+ var obj = {}
+ obj.a = obj
+ obj.b = {self: obj}
+
+ var decycle = Sinon.spy(function() { return decycle.callCount })
+ var json = stringify(obj, null, 2, decycle)
+ json.must.eql(jsonify({a: 1, b: {self: 2}}))
+
+ decycle.callCount.must.equal(2)
+ decycle.args[0][0].must.equal("a")
+ decycle.args[0][1].must.equal(obj)
+ decycle.args[1][0].must.equal("self")
+ decycle.args[1][1].must.equal(obj)
+ })
+
+ it("must use decycler's output when it returned null", function() {
+ var obj = {a: "b"}
+ obj.self = obj
+ obj.selves = [obj, obj]
+
+ function decycle() { return null }
+ stringify(obj, null, 2, decycle).must.eql(jsonify({
+ a: "b",
+ self: null,
+ selves: [null, null]
+ }))
+ })
+
+ it("must use decycler's output when it returned undefined", function() {
+ var obj = {a: "b"}
+ obj.self = obj
+ obj.selves = [obj, obj]
+
+ function decycle() {}
+ stringify(obj, null, 2, decycle).must.eql(jsonify({
+ a: "b",
+ selves: [null, null]
+ }))
+ })
+
+ it("must throw given a decycler that returns a cycle", function() {
+ var obj = {}
+ obj.self = obj
+ var err
+ function identity(key, value) { return value }
+ try { stringify(obj, null, 2, identity) } catch (ex) { err = ex }
+ err.must.be.an.instanceof(TypeError)
+ })
+
+ describe(".getSerialize", function() {
+ it("must stringify circular objects", function() {
+ var obj = {a: "b"}
+ obj.circularRef = obj
+ obj.list = [obj, obj]
+
+ var json = JSON.stringify(obj, stringify.getSerialize(), 2)
+ json.must.eql(jsonify({
+ "a": "b",
+ "circularRef": "[Circular ~]",
+ "list": ["[Circular ~]", "[Circular ~]"]
+ }))
+ })
+
+ // This is the behavior as of Mar 3, 2015.
+ // The serializer function keeps state inside the returned function and
+ // so far I'm not sure how to not do that. JSON.stringify's replacer is not
+ // called _after_ serialization.
+ xit("must return a function that could be called twice", function() {
+ var obj = {name: "Alice"}
+ obj.self = obj
+
+ var json
+ var serializer = stringify.getSerialize()
+
+ json = JSON.stringify(obj, serializer, 2)
+ json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"}))
+
+ json = JSON.stringify(obj, serializer, 2)
+ json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"}))
+ })
+ })
+})
+
+function bangString(key, value) {
+ return typeof value == "string" ? value + "!" : value
+}
--- /dev/null
+2.0.12 / 2015-05-19
+===================
+
+ * deps: mime-db@~1.10.0
+ - Add new mime types
+
+2.0.11 / 2015-05-05
+===================
+
+ * deps: mime-db@~1.9.1
+ - Add new mime types
+
+2.0.10 / 2015-03-13
+===================
+
+ * deps: mime-db@~1.8.0
+ - Add new mime types
+
+2.0.9 / 2015-02-09
+==================
+
+ * deps: mime-db@~1.7.0
+ - Add new mime types
+ - Community extensions ownership transferred from `node-mime`
+
+2.0.8 / 2015-01-29
+==================
+
+ * deps: mime-db@~1.6.0
+ - Add new mime types
+
+2.0.7 / 2014-12-30
+==================
+
+ * deps: mime-db@~1.5.0
+ - Add new mime types
+ - Fix various invalid MIME type entries
+
+2.0.6 / 2014-12-30
+==================
+
+ * deps: mime-db@~1.4.0
+ - Add new mime types
+ - Fix various invalid MIME type entries
+ - Remove example template MIME types
+
+2.0.5 / 2014-12-29
+==================
+
+ * deps: mime-db@~1.3.1
+ - Fix missing extensions
+
+2.0.4 / 2014-12-10
+==================
+
+ * deps: mime-db@~1.3.0
+ - Add new mime types
+
+2.0.3 / 2014-11-09
+==================
+
+ * deps: mime-db@~1.2.0
+ - Add new mime types
+
+2.0.2 / 2014-09-28
+==================
+
+ * deps: mime-db@~1.1.0
+ - Add new mime types
+ - Add additional compressible
+ - Update charsets
+
+2.0.1 / 2014-09-07
+==================
+
+ * Support Node.js 0.6
+
+2.0.0 / 2014-09-02
+==================
+
+ * Use `mime-db`
+ * Remove `.define()`
+
+1.0.2 / 2014-08-04
+==================
+
+ * Set charset=utf-8 for `text/javascript`
+
+1.0.1 / 2014-06-24
+==================
+
+ * Add `text/jsx` type
+
+1.0.0 / 2014-05-12
+==================
+
+ * Return `false` for unknown types
+ * Set charset=utf-8 for `application/json`
+
+0.1.0 / 2014-05-02
+==================
+
+ * Initial release
--- /dev/null
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Jonathan Ong me@jongleberry.com
+
+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.
--- /dev/null
+# mime-types
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Node.js Version][node-version-image]][node-version-url]
+[![Build Status][travis-image]][travis-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+
+The ultimate javascript content-type utility.
+
+Similar to [node-mime](https://github.com/broofa/node-mime), except:
+
+- __No fallbacks.__ Instead of naively returning the first available type, `mime-types` simply returns `false`,
+ so do `var type = mime.lookup('unrecognized') || 'application/octet-stream'`.
+- No `new Mime()` business, so you could do `var lookup = require('mime-types').lookup`.
+- Additional mime types are added such as jade and stylus via [mime-db](https://github.com/jshttp/mime-db)
+- No `.define()` functionality
+
+Otherwise, the API is compatible.
+
+## Install
+
+```sh
+$ npm install mime-types
+```
+
+## Adding Types
+
+All mime types are based on [mime-db](https://github.com/jshttp/mime-db),
+so open a PR there if you'd like to add mime types.
+
+## API
+
+```js
+var mime = require('mime-types')
+```
+
+All functions return `false` if input is invalid or not found.
+
+### mime.lookup(path)
+
+Lookup the content-type associated with a file.
+
+```js
+mime.lookup('json') // 'application/json'
+mime.lookup('.md') // 'text/x-markdown'
+mime.lookup('file.html') // 'text/html'
+mime.lookup('folder/file.js') // 'application/javascript'
+
+mime.lookup('cats') // false
+```
+
+### mime.contentType(type)
+
+Create a full content-type header given a content-type or extension.
+
+```js
+mime.contentType('markdown') // 'text/x-markdown; charset=utf-8'
+mime.contentType('file.json') // 'application/json; charset=utf-8'
+
+// from a full path
+mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8'
+```
+
+### mime.extension(type)
+
+Get the default extension for a content-type.
+
+```js
+mime.extension('application/octet-stream') // 'bin'
+```
+
+### mime.charset(type)
+
+Lookup the implied default charset of a content-type.
+
+```js
+mime.charset('text/x-markdown') // 'UTF-8'
+```
+
+### var type = mime.types[extension]
+
+A map of content-types by extension.
+
+### [extensions...] = mime.extensions[type]
+
+A map of extensions by content-type.
+
+## License
+
+[MIT](LICENSE)
+
+[npm-image]: https://img.shields.io/npm/v/mime-types.svg
+[npm-url]: https://npmjs.org/package/mime-types
+[node-version-image]: https://img.shields.io/node/v/mime-types.svg
+[node-version-url]: http://nodejs.org/download/
+[travis-image]: https://img.shields.io/travis/jshttp/mime-types/master.svg
+[travis-url]: https://travis-ci.org/jshttp/mime-types
+[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-types/master.svg
+[coveralls-url]: https://coveralls.io/r/jshttp/mime-types
+[downloads-image]: https://img.shields.io/npm/dm/mime-types.svg
+[downloads-url]: https://npmjs.org/package/mime-types
--- /dev/null
+
+var db = require('mime-db')
+
+// types[extension] = type
+exports.types = Object.create(null)
+// extensions[type] = [extensions]
+exports.extensions = Object.create(null)
+
+Object.keys(db).forEach(function (name) {
+ var mime = db[name]
+ var exts = mime.extensions
+ if (!exts || !exts.length) return
+ exports.extensions[name] = exts
+ exts.forEach(function (ext) {
+ exports.types[ext] = name
+ })
+})
+
+exports.lookup = function (string) {
+ if (!string || typeof string !== "string") return false
+ // remove any leading paths, though we should just use path.basename
+ string = string.replace(/.*[\.\/\\]/, '').toLowerCase()
+ if (!string) return false
+ return exports.types[string] || false
+}
+
+exports.extension = function (type) {
+ if (!type || typeof type !== "string") return false
+ // to do: use media-typer
+ type = type.match(/^\s*([^;\s]*)(?:;|\s|$)/)
+ if (!type) return false
+ var exts = exports.extensions[type[1].toLowerCase()]
+ if (!exts || !exts.length) return false
+ return exts[0]
+}
+
+// type has to be an exact mime type
+exports.charset = function (type) {
+ var mime = db[type]
+ if (mime && mime.charset) return mime.charset
+
+ // default text/* to utf-8
+ if (/^text\//.test(type)) return 'UTF-8'
+
+ return false
+}
+
+// backwards compatibility
+exports.charsets = {
+ lookup: exports.charset
+}
+
+// to do: maybe use set-type module or something
+exports.contentType = function (type) {
+ if (!type || typeof type !== "string") return false
+ if (!~type.indexOf('/')) type = exports.lookup(type)
+ if (!type) return false
+ if (!~type.indexOf('charset')) {
+ var charset = exports.charset(type)
+ if (charset) type += '; charset=' + charset.toLowerCase()
+ }
+ return type
+}
--- /dev/null
+1.10.0 / 2015-05-19
+===================
+
+ * Add `application/vnd.balsamiq.bmpr`
+ * Add `application/vnd.microsoft.portable-executable`
+ * Add `application/x-ns-proxy-autoconfig`
+
+1.9.1 / 2015-04-19
+==================
+
+ * Remove `.json` extension from `application/manifest+json`
+ - This is causing bugs downstream
+
+1.9.0 / 2015-04-19
+==================
+
+ * Add `application/manifest+json`
+ * Add `application/vnd.micro+json`
+ * Add `image/vnd.zbrush.pcx`
+ * Add `image/x-ms-bmp`
+
+1.8.0 / 2015-03-13
+==================
+
+ * Add `application/vnd.citationstyles.style+xml`
+ * Add `application/vnd.fastcopy-disk-image`
+ * Add `application/vnd.gov.sk.xmldatacontainer+xml`
+ * Add extension `.jsonld` to `application/ld+json`
+
+1.7.0 / 2015-02-08
+==================
+
+ * Add `application/vnd.gerber`
+ * Add `application/vnd.msa-disk-image`
+
+1.6.1 / 2015-02-05
+==================
+
+ * Community extensions ownership transferred from `node-mime`
+
+1.6.0 / 2015-01-29
+==================
+
+ * Add `application/jose`
+ * Add `application/jose+json`
+ * Add `application/json-seq`
+ * Add `application/jwk+json`
+ * Add `application/jwk-set+json`
+ * Add `application/jwt`
+ * Add `application/rdap+json`
+ * Add `application/vnd.gov.sk.e-form+xml`
+ * Add `application/vnd.ims.imsccv1p3`
+
+1.5.0 / 2014-12-30
+==================
+
+ * Add `application/vnd.oracle.resource+json`
+ * Fix various invalid MIME type entries
+ - `application/mbox+xml`
+ - `application/oscp-response`
+ - `application/vwg-multiplexed`
+ - `audio/g721`
+
+1.4.0 / 2014-12-21
+==================
+
+ * Add `application/vnd.ims.imsccv1p2`
+ * Fix various invalid MIME type entries
+ - `application/vnd-acucobol`
+ - `application/vnd-curl`
+ - `application/vnd-dart`
+ - `application/vnd-dxr`
+ - `application/vnd-fdf`
+ - `application/vnd-mif`
+ - `application/vnd-sema`
+ - `application/vnd-wap-wmlc`
+ - `application/vnd.adobe.flash-movie`
+ - `application/vnd.dece-zip`
+ - `application/vnd.dvb_service`
+ - `application/vnd.micrografx-igx`
+ - `application/vnd.sealed-doc`
+ - `application/vnd.sealed-eml`
+ - `application/vnd.sealed-mht`
+ - `application/vnd.sealed-ppt`
+ - `application/vnd.sealed-tiff`
+ - `application/vnd.sealed-xls`
+ - `application/vnd.sealedmedia.softseal-html`
+ - `application/vnd.sealedmedia.softseal-pdf`
+ - `application/vnd.wap-slc`
+ - `application/vnd.wap-wbxml`
+ - `audio/vnd.sealedmedia.softseal-mpeg`
+ - `image/vnd-djvu`
+ - `image/vnd-svf`
+ - `image/vnd-wap-wbmp`
+ - `image/vnd.sealed-png`
+ - `image/vnd.sealedmedia.softseal-gif`
+ - `image/vnd.sealedmedia.softseal-jpg`
+ - `model/vnd-dwf`
+ - `model/vnd.parasolid.transmit-binary`
+ - `model/vnd.parasolid.transmit-text`
+ - `text/vnd-a`
+ - `text/vnd-curl`
+ - `text/vnd.wap-wml`
+ * Remove example template MIME types
+ - `application/example`
+ - `audio/example`
+ - `image/example`
+ - `message/example`
+ - `model/example`
+ - `multipart/example`
+ - `text/example`
+ - `video/example`
+
+1.3.1 / 2014-12-16
+==================
+
+ * Fix missing extensions
+ - `application/json5`
+ - `text/hjson`
+
+1.3.0 / 2014-12-07
+==================
+
+ * Add `application/a2l`
+ * Add `application/aml`
+ * Add `application/atfx`
+ * Add `application/atxml`
+ * Add `application/cdfx+xml`
+ * Add `application/dii`
+ * Add `application/json5`
+ * Add `application/lxf`
+ * Add `application/mf4`
+ * Add `application/vnd.apache.thrift.compact`
+ * Add `application/vnd.apache.thrift.json`
+ * Add `application/vnd.coffeescript`
+ * Add `application/vnd.enphase.envoy`
+ * Add `application/vnd.ims.imsccv1p1`
+ * Add `text/csv-schema`
+ * Add `text/hjson`
+ * Add `text/markdown`
+ * Add `text/yaml`
+
+1.2.0 / 2014-11-09
+==================
+
+ * Add `application/cea`
+ * Add `application/dit`
+ * Add `application/vnd.gov.sk.e-form+zip`
+ * Add `application/vnd.tmd.mediaflex.api+xml`
+ * Type `application/epub+zip` is now IANA-registered
+
+1.1.2 / 2014-10-23
+==================
+
+ * Rebuild database for `application/x-www-form-urlencoded` change
+
+1.1.1 / 2014-10-20
+==================
+
+ * Mark `application/x-www-form-urlencoded` as compressible.
+
+1.1.0 / 2014-09-28
+==================
+
+ * Add `application/font-woff2`
+
+1.0.3 / 2014-09-25
+==================
+
+ * Fix engine requirement in package
+
+1.0.2 / 2014-09-25
+==================
+
+ * Add `application/coap-group+json`
+ * Add `application/dcd`
+ * Add `application/vnd.apache.thrift.binary`
+ * Add `image/vnd.tencent.tap`
+ * Mark all JSON-derived types as compressible
+ * Update `text/vtt` data
+
+1.0.1 / 2014-08-30
+==================
+
+ * Fix extension ordering
+
+1.0.0 / 2014-08-30
+==================
+
+ * Add `application/atf`
+ * Add `application/merge-patch+json`
+ * Add `multipart/x-mixed-replace`
+ * Add `source: 'apache'` metadata
+ * Add `source: 'iana'` metadata
+ * Remove badly-assumed charset data
--- /dev/null
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Jonathan Ong me@jongleberry.com
+
+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.
--- /dev/null
+# mime-db
+
+[![NPM Version][npm-version-image]][npm-url]
+[![NPM Downloads][npm-downloads-image]][npm-url]
+[![Node.js Version][node-image]][node-url]
+[![Build Status][travis-image]][travis-url]
+[![Coverage Status][coveralls-image]][coveralls-url]
+
+This is a database of all mime types.
+It consists of a single, public JSON file and does not include any logic,
+allowing it to remain as un-opinionated as possible with an API.
+It aggregates data from the following sources:
+
+- http://www.iana.org/assignments/media-types/media-types.xhtml
+- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+
+## Installation
+
+```bash
+npm install mime-db
+```
+
+If you're crazy enough to use this in the browser,
+you can just grab the JSON file:
+
+```
+https://cdn.rawgit.com/jshttp/mime-db/master/db.json
+```
+
+## Usage
+
+```js
+var db = require('mime-db');
+
+// grab data on .js files
+var data = db['application/javascript'];
+```
+
+## Data Structure
+
+The JSON file is a map lookup for lowercased mime types.
+Each mime type has the following properties:
+
+- `.source` - where the mime type is defined.
+ If not set, it's probably a custom media type.
+ - `apache` - [Apache common media types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types)
+ - `iana` - [IANA-defined media types](http://www.iana.org/assignments/media-types/media-types.xhtml)
+- `.extensions[]` - known extensions associated with this mime type.
+- `.compressible` - whether a file of this type is can be gzipped.
+- `.charset` - the default charset associated with this type, if any.
+
+If unknown, every property could be `undefined`.
+
+## Contributing
+
+To edit the database, only make PRs against `src/custom.json` or
+`src/custom-suffix.json`.
+
+To update the build, run `npm run update`.
+
+## Adding Custom Media Types
+
+The best way to get new media types included in this library is to register
+them with the IANA. The community registration procedure is outlined in
+[RFC 6838 section 5](http://tools.ietf.org/html/rfc6838#section-5). Types
+registered with the IANA are automatically pulled into this library.
+
+[npm-version-image]: https://img.shields.io/npm/v/mime-db.svg
+[npm-downloads-image]: https://img.shields.io/npm/dm/mime-db.svg
+[npm-url]: https://npmjs.org/package/mime-db
+[travis-image]: https://img.shields.io/travis/jshttp/mime-db/master.svg
+[travis-url]: https://travis-ci.org/jshttp/mime-db
+[coveralls-image]: https://img.shields.io/coveralls/jshttp/mime-db/master.svg
+[coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master
+[node-image]: https://img.shields.io/node/v/mime-db.svg
+[node-url]: http://nodejs.org/download/
--- /dev/null
+{
+ "application/1d-interleaved-parityfec": {
+ "source": "iana"
+ },
+ "application/3gpdash-qoe-report+xml": {
+ "source": "iana"
+ },
+ "application/3gpp-ims+xml": {
+ "source": "iana"
+ },
+ "application/a2l": {
+ "source": "iana"
+ },
+ "application/activemessage": {
+ "source": "iana"
+ },
+ "application/alto-costmap+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-costmapfilter+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-directory+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-endpointcost+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-endpointcostparams+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-endpointprop+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-endpointpropparams+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-error+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-networkmap+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/alto-networkmapfilter+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/aml": {
+ "source": "iana"
+ },
+ "application/andrew-inset": {
+ "source": "iana",
+ "extensions": ["ez"]
+ },
+ "application/applefile": {
+ "source": "iana"
+ },
+ "application/applixware": {
+ "source": "apache",
+ "extensions": ["aw"]
+ },
+ "application/atf": {
+ "source": "iana"
+ },
+ "application/atfx": {
+ "source": "iana"
+ },
+ "application/atom+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["atom"]
+ },
+ "application/atomcat+xml": {
+ "source": "iana",
+ "extensions": ["atomcat"]
+ },
+ "application/atomdeleted+xml": {
+ "source": "iana"
+ },
+ "application/atomicmail": {
+ "source": "iana"
+ },
+ "application/atomsvc+xml": {
+ "source": "iana",
+ "extensions": ["atomsvc"]
+ },
+ "application/atxml": {
+ "source": "iana"
+ },
+ "application/auth-policy+xml": {
+ "source": "iana"
+ },
+ "application/bacnet-xdd+zip": {
+ "source": "iana"
+ },
+ "application/batch-smtp": {
+ "source": "iana"
+ },
+ "application/beep+xml": {
+ "source": "iana"
+ },
+ "application/calendar+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/calendar+xml": {
+ "source": "iana"
+ },
+ "application/call-completion": {
+ "source": "iana"
+ },
+ "application/cals-1840": {
+ "source": "iana"
+ },
+ "application/cbor": {
+ "source": "iana"
+ },
+ "application/ccmp+xml": {
+ "source": "iana"
+ },
+ "application/ccxml+xml": {
+ "source": "iana",
+ "extensions": ["ccxml"]
+ },
+ "application/cdfx+xml": {
+ "source": "iana"
+ },
+ "application/cdmi-capability": {
+ "source": "iana",
+ "extensions": ["cdmia"]
+ },
+ "application/cdmi-container": {
+ "source": "iana",
+ "extensions": ["cdmic"]
+ },
+ "application/cdmi-domain": {
+ "source": "iana",
+ "extensions": ["cdmid"]
+ },
+ "application/cdmi-object": {
+ "source": "iana",
+ "extensions": ["cdmio"]
+ },
+ "application/cdmi-queue": {
+ "source": "iana",
+ "extensions": ["cdmiq"]
+ },
+ "application/cea": {
+ "source": "iana"
+ },
+ "application/cea-2018+xml": {
+ "source": "iana"
+ },
+ "application/cellml+xml": {
+ "source": "iana"
+ },
+ "application/cfw": {
+ "source": "iana"
+ },
+ "application/cms": {
+ "source": "iana"
+ },
+ "application/cnrp+xml": {
+ "source": "iana"
+ },
+ "application/coap-group+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/commonground": {
+ "source": "iana"
+ },
+ "application/conference-info+xml": {
+ "source": "iana"
+ },
+ "application/cpl+xml": {
+ "source": "iana"
+ },
+ "application/csrattrs": {
+ "source": "iana"
+ },
+ "application/csta+xml": {
+ "source": "iana"
+ },
+ "application/cstadata+xml": {
+ "source": "iana"
+ },
+ "application/cu-seeme": {
+ "source": "apache",
+ "extensions": ["cu"]
+ },
+ "application/cybercash": {
+ "source": "iana"
+ },
+ "application/dart": {
+ "compressible": true
+ },
+ "application/dash+xml": {
+ "source": "iana",
+ "extensions": ["mdp"]
+ },
+ "application/dashdelta": {
+ "source": "iana"
+ },
+ "application/davmount+xml": {
+ "source": "iana",
+ "extensions": ["davmount"]
+ },
+ "application/dca-rft": {
+ "source": "iana"
+ },
+ "application/dcd": {
+ "source": "iana"
+ },
+ "application/dec-dx": {
+ "source": "iana"
+ },
+ "application/dialog-info+xml": {
+ "source": "iana"
+ },
+ "application/dicom": {
+ "source": "iana"
+ },
+ "application/dii": {
+ "source": "iana"
+ },
+ "application/dit": {
+ "source": "iana"
+ },
+ "application/dns": {
+ "source": "iana"
+ },
+ "application/docbook+xml": {
+ "source": "apache",
+ "extensions": ["dbk"]
+ },
+ "application/dskpp+xml": {
+ "source": "iana"
+ },
+ "application/dssc+der": {
+ "source": "iana",
+ "extensions": ["dssc"]
+ },
+ "application/dssc+xml": {
+ "source": "iana",
+ "extensions": ["xdssc"]
+ },
+ "application/dvcs": {
+ "source": "iana"
+ },
+ "application/ecmascript": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["ecma"]
+ },
+ "application/edi-consent": {
+ "source": "iana"
+ },
+ "application/edi-x12": {
+ "source": "iana",
+ "compressible": false
+ },
+ "application/edifact": {
+ "source": "iana",
+ "compressible": false
+ },
+ "application/emma+xml": {
+ "source": "iana",
+ "extensions": ["emma"]
+ },
+ "application/emotionml+xml": {
+ "source": "iana"
+ },
+ "application/encaprtp": {
+ "source": "iana"
+ },
+ "application/epp+xml": {
+ "source": "iana"
+ },
+ "application/epub+zip": {
+ "source": "iana",
+ "extensions": ["epub"]
+ },
+ "application/eshop": {
+ "source": "iana"
+ },
+ "application/exi": {
+ "source": "iana",
+ "extensions": ["exi"]
+ },
+ "application/fastinfoset": {
+ "source": "iana"
+ },
+ "application/fastsoap": {
+ "source": "iana"
+ },
+ "application/fdt+xml": {
+ "source": "iana"
+ },
+ "application/fits": {
+ "source": "iana"
+ },
+ "application/font-sfnt": {
+ "source": "iana"
+ },
+ "application/font-tdpfr": {
+ "source": "iana",
+ "extensions": ["pfr"]
+ },
+ "application/font-woff": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["woff"]
+ },
+ "application/font-woff2": {
+ "compressible": false,
+ "extensions": ["woff2"]
+ },
+ "application/framework-attributes+xml": {
+ "source": "iana"
+ },
+ "application/gml+xml": {
+ "source": "apache",
+ "extensions": ["gml"]
+ },
+ "application/gpx+xml": {
+ "source": "apache",
+ "extensions": ["gpx"]
+ },
+ "application/gxf": {
+ "source": "apache",
+ "extensions": ["gxf"]
+ },
+ "application/gzip": {
+ "source": "iana",
+ "compressible": false
+ },
+ "application/h224": {
+ "source": "iana"
+ },
+ "application/held+xml": {
+ "source": "iana"
+ },
+ "application/http": {
+ "source": "iana"
+ },
+ "application/hyperstudio": {
+ "source": "iana",
+ "extensions": ["stk"]
+ },
+ "application/ibe-key-request+xml": {
+ "source": "iana"
+ },
+ "application/ibe-pkg-reply+xml": {
+ "source": "iana"
+ },
+ "application/ibe-pp-data": {
+ "source": "iana"
+ },
+ "application/iges": {
+ "source": "iana"
+ },
+ "application/im-iscomposing+xml": {
+ "source": "iana"
+ },
+ "application/index": {
+ "source": "iana"
+ },
+ "application/index.cmd": {
+ "source": "iana"
+ },
+ "application/index.obj": {
+ "source": "iana"
+ },
+ "application/index.response": {
+ "source": "iana"
+ },
+ "application/index.vnd": {
+ "source": "iana"
+ },
+ "application/inkml+xml": {
+ "source": "iana",
+ "extensions": ["ink","inkml"]
+ },
+ "application/iotp": {
+ "source": "iana"
+ },
+ "application/ipfix": {
+ "source": "iana",
+ "extensions": ["ipfix"]
+ },
+ "application/ipp": {
+ "source": "iana"
+ },
+ "application/isup": {
+ "source": "iana"
+ },
+ "application/its+xml": {
+ "source": "iana"
+ },
+ "application/java-archive": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["jar"]
+ },
+ "application/java-serialized-object": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["ser"]
+ },
+ "application/java-vm": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["class"]
+ },
+ "application/javascript": {
+ "source": "iana",
+ "charset": "UTF-8",
+ "compressible": true,
+ "extensions": ["js"]
+ },
+ "application/jose": {
+ "source": "iana"
+ },
+ "application/jose+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/jrd+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/json": {
+ "source": "iana",
+ "charset": "UTF-8",
+ "compressible": true,
+ "extensions": ["json","map"]
+ },
+ "application/json-patch+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/json-seq": {
+ "source": "iana"
+ },
+ "application/json5": {
+ "extensions": ["json5"]
+ },
+ "application/jsonml+json": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["jsonml"]
+ },
+ "application/jwk+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/jwk-set+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/jwt": {
+ "source": "iana"
+ },
+ "application/kpml-request+xml": {
+ "source": "iana"
+ },
+ "application/kpml-response+xml": {
+ "source": "iana"
+ },
+ "application/ld+json": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["jsonld"]
+ },
+ "application/link-format": {
+ "source": "iana"
+ },
+ "application/load-control+xml": {
+ "source": "iana"
+ },
+ "application/lost+xml": {
+ "source": "iana",
+ "extensions": ["lostxml"]
+ },
+ "application/lostsync+xml": {
+ "source": "iana"
+ },
+ "application/lxf": {
+ "source": "iana"
+ },
+ "application/mac-binhex40": {
+ "source": "iana",
+ "extensions": ["hqx"]
+ },
+ "application/mac-compactpro": {
+ "source": "apache",
+ "extensions": ["cpt"]
+ },
+ "application/macwriteii": {
+ "source": "iana"
+ },
+ "application/mads+xml": {
+ "source": "iana",
+ "extensions": ["mads"]
+ },
+ "application/manifest+json": {
+ "charset": "UTF-8",
+ "compressible": true,
+ "extensions": ["webmanifest"]
+ },
+ "application/marc": {
+ "source": "iana",
+ "extensions": ["mrc"]
+ },
+ "application/marcxml+xml": {
+ "source": "iana",
+ "extensions": ["mrcx"]
+ },
+ "application/mathematica": {
+ "source": "iana",
+ "extensions": ["ma","nb","mb"]
+ },
+ "application/mathml+xml": {
+ "source": "iana",
+ "extensions": ["mathml"]
+ },
+ "application/mathml-content+xml": {
+ "source": "iana"
+ },
+ "application/mathml-presentation+xml": {
+ "source": "iana"
+ },
+ "application/mbms-associated-procedure-description+xml": {
+ "source": "iana"
+ },
+ "application/mbms-deregister+xml": {
+ "source": "iana"
+ },
+ "application/mbms-envelope+xml": {
+ "source": "iana"
+ },
+ "application/mbms-msk+xml": {
+ "source": "iana"
+ },
+ "application/mbms-msk-response+xml": {
+ "source": "iana"
+ },
+ "application/mbms-protection-description+xml": {
+ "source": "iana"
+ },
+ "application/mbms-reception-report+xml": {
+ "source": "iana"
+ },
+ "application/mbms-register+xml": {
+ "source": "iana"
+ },
+ "application/mbms-register-response+xml": {
+ "source": "iana"
+ },
+ "application/mbms-schedule+xml": {
+ "source": "iana"
+ },
+ "application/mbms-user-service-description+xml": {
+ "source": "iana"
+ },
+ "application/mbox": {
+ "source": "iana",
+ "extensions": ["mbox"]
+ },
+ "application/media-policy-dataset+xml": {
+ "source": "iana"
+ },
+ "application/media_control+xml": {
+ "source": "iana"
+ },
+ "application/mediaservercontrol+xml": {
+ "source": "iana",
+ "extensions": ["mscml"]
+ },
+ "application/merge-patch+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/metalink+xml": {
+ "source": "apache",
+ "extensions": ["metalink"]
+ },
+ "application/metalink4+xml": {
+ "source": "iana",
+ "extensions": ["meta4"]
+ },
+ "application/mets+xml": {
+ "source": "iana",
+ "extensions": ["mets"]
+ },
+ "application/mf4": {
+ "source": "iana"
+ },
+ "application/mikey": {
+ "source": "iana"
+ },
+ "application/mods+xml": {
+ "source": "iana",
+ "extensions": ["mods"]
+ },
+ "application/moss-keys": {
+ "source": "iana"
+ },
+ "application/moss-signature": {
+ "source": "iana"
+ },
+ "application/mosskey-data": {
+ "source": "iana"
+ },
+ "application/mosskey-request": {
+ "source": "iana"
+ },
+ "application/mp21": {
+ "source": "iana",
+ "extensions": ["m21","mp21"]
+ },
+ "application/mp4": {
+ "source": "iana",
+ "extensions": ["mp4s","m4p"]
+ },
+ "application/mpeg4-generic": {
+ "source": "iana"
+ },
+ "application/mpeg4-iod": {
+ "source": "iana"
+ },
+ "application/mpeg4-iod-xmt": {
+ "source": "iana"
+ },
+ "application/mrb-consumer+xml": {
+ "source": "iana"
+ },
+ "application/mrb-publish+xml": {
+ "source": "iana"
+ },
+ "application/msc-ivr+xml": {
+ "source": "iana"
+ },
+ "application/msc-mixer+xml": {
+ "source": "iana"
+ },
+ "application/msword": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["doc","dot"]
+ },
+ "application/mxf": {
+ "source": "iana",
+ "extensions": ["mxf"]
+ },
+ "application/nasdata": {
+ "source": "iana"
+ },
+ "application/news-checkgroups": {
+ "source": "iana"
+ },
+ "application/news-groupinfo": {
+ "source": "iana"
+ },
+ "application/news-transmission": {
+ "source": "iana"
+ },
+ "application/nlsml+xml": {
+ "source": "iana"
+ },
+ "application/nss": {
+ "source": "iana"
+ },
+ "application/ocsp-request": {
+ "source": "iana"
+ },
+ "application/ocsp-response": {
+ "source": "iana"
+ },
+ "application/octet-stream": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","buffer"]
+ },
+ "application/oda": {
+ "source": "iana",
+ "extensions": ["oda"]
+ },
+ "application/odx": {
+ "source": "iana"
+ },
+ "application/oebps-package+xml": {
+ "source": "iana",
+ "extensions": ["opf"]
+ },
+ "application/ogg": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["ogx"]
+ },
+ "application/omdoc+xml": {
+ "source": "apache",
+ "extensions": ["omdoc"]
+ },
+ "application/onenote": {
+ "source": "apache",
+ "extensions": ["onetoc","onetoc2","onetmp","onepkg"]
+ },
+ "application/oxps": {
+ "source": "iana",
+ "extensions": ["oxps"]
+ },
+ "application/p2p-overlay+xml": {
+ "source": "iana"
+ },
+ "application/parityfec": {
+ "source": "iana"
+ },
+ "application/patch-ops-error+xml": {
+ "source": "iana",
+ "extensions": ["xer"]
+ },
+ "application/pdf": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["pdf"]
+ },
+ "application/pdx": {
+ "source": "iana"
+ },
+ "application/pgp-encrypted": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["pgp"]
+ },
+ "application/pgp-keys": {
+ "source": "iana"
+ },
+ "application/pgp-signature": {
+ "source": "iana",
+ "extensions": ["asc","sig"]
+ },
+ "application/pics-rules": {
+ "source": "apache",
+ "extensions": ["prf"]
+ },
+ "application/pidf+xml": {
+ "source": "iana"
+ },
+ "application/pidf-diff+xml": {
+ "source": "iana"
+ },
+ "application/pkcs10": {
+ "source": "iana",
+ "extensions": ["p10"]
+ },
+ "application/pkcs7-mime": {
+ "source": "iana",
+ "extensions": ["p7m","p7c"]
+ },
+ "application/pkcs7-signature": {
+ "source": "iana",
+ "extensions": ["p7s"]
+ },
+ "application/pkcs8": {
+ "source": "iana",
+ "extensions": ["p8"]
+ },
+ "application/pkix-attr-cert": {
+ "source": "iana",
+ "extensions": ["ac"]
+ },
+ "application/pkix-cert": {
+ "source": "iana",
+ "extensions": ["cer"]
+ },
+ "application/pkix-crl": {
+ "source": "iana",
+ "extensions": ["crl"]
+ },
+ "application/pkix-pkipath": {
+ "source": "iana",
+ "extensions": ["pkipath"]
+ },
+ "application/pkixcmp": {
+ "source": "iana",
+ "extensions": ["pki"]
+ },
+ "application/pls+xml": {
+ "source": "iana",
+ "extensions": ["pls"]
+ },
+ "application/poc-settings+xml": {
+ "source": "iana"
+ },
+ "application/postscript": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["ai","eps","ps"]
+ },
+ "application/provenance+xml": {
+ "source": "iana"
+ },
+ "application/prs.alvestrand.titrax-sheet": {
+ "source": "iana"
+ },
+ "application/prs.cww": {
+ "source": "iana",
+ "extensions": ["cww"]
+ },
+ "application/prs.hpub+zip": {
+ "source": "iana"
+ },
+ "application/prs.nprend": {
+ "source": "iana"
+ },
+ "application/prs.plucker": {
+ "source": "iana"
+ },
+ "application/prs.rdf-xml-crypt": {
+ "source": "iana"
+ },
+ "application/prs.xsf+xml": {
+ "source": "iana"
+ },
+ "application/pskc+xml": {
+ "source": "iana",
+ "extensions": ["pskcxml"]
+ },
+ "application/qsig": {
+ "source": "iana"
+ },
+ "application/raptorfec": {
+ "source": "iana"
+ },
+ "application/rdap+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/rdf+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["rdf"]
+ },
+ "application/reginfo+xml": {
+ "source": "iana",
+ "extensions": ["rif"]
+ },
+ "application/relax-ng-compact-syntax": {
+ "source": "iana",
+ "extensions": ["rnc"]
+ },
+ "application/remote-printing": {
+ "source": "iana"
+ },
+ "application/reputon+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/resource-lists+xml": {
+ "source": "iana",
+ "extensions": ["rl"]
+ },
+ "application/resource-lists-diff+xml": {
+ "source": "iana",
+ "extensions": ["rld"]
+ },
+ "application/riscos": {
+ "source": "iana"
+ },
+ "application/rlmi+xml": {
+ "source": "iana"
+ },
+ "application/rls-services+xml": {
+ "source": "iana",
+ "extensions": ["rs"]
+ },
+ "application/rpki-ghostbusters": {
+ "source": "iana",
+ "extensions": ["gbr"]
+ },
+ "application/rpki-manifest": {
+ "source": "iana",
+ "extensions": ["mft"]
+ },
+ "application/rpki-roa": {
+ "source": "iana",
+ "extensions": ["roa"]
+ },
+ "application/rpki-updown": {
+ "source": "iana"
+ },
+ "application/rsd+xml": {
+ "source": "apache",
+ "extensions": ["rsd"]
+ },
+ "application/rss+xml": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["rss"]
+ },
+ "application/rtf": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["rtf"]
+ },
+ "application/rtploopback": {
+ "source": "iana"
+ },
+ "application/rtx": {
+ "source": "iana"
+ },
+ "application/samlassertion+xml": {
+ "source": "iana"
+ },
+ "application/samlmetadata+xml": {
+ "source": "iana"
+ },
+ "application/sbml+xml": {
+ "source": "iana",
+ "extensions": ["sbml"]
+ },
+ "application/scaip+xml": {
+ "source": "iana"
+ },
+ "application/scvp-cv-request": {
+ "source": "iana",
+ "extensions": ["scq"]
+ },
+ "application/scvp-cv-response": {
+ "source": "iana",
+ "extensions": ["scs"]
+ },
+ "application/scvp-vp-request": {
+ "source": "iana",
+ "extensions": ["spq"]
+ },
+ "application/scvp-vp-response": {
+ "source": "iana",
+ "extensions": ["spp"]
+ },
+ "application/sdp": {
+ "source": "iana",
+ "extensions": ["sdp"]
+ },
+ "application/sep+xml": {
+ "source": "iana"
+ },
+ "application/sep-exi": {
+ "source": "iana"
+ },
+ "application/session-info": {
+ "source": "iana"
+ },
+ "application/set-payment": {
+ "source": "iana"
+ },
+ "application/set-payment-initiation": {
+ "source": "iana",
+ "extensions": ["setpay"]
+ },
+ "application/set-registration": {
+ "source": "iana"
+ },
+ "application/set-registration-initiation": {
+ "source": "iana",
+ "extensions": ["setreg"]
+ },
+ "application/sgml": {
+ "source": "iana"
+ },
+ "application/sgml-open-catalog": {
+ "source": "iana"
+ },
+ "application/shf+xml": {
+ "source": "iana",
+ "extensions": ["shf"]
+ },
+ "application/sieve": {
+ "source": "iana"
+ },
+ "application/simple-filter+xml": {
+ "source": "iana"
+ },
+ "application/simple-message-summary": {
+ "source": "iana"
+ },
+ "application/simplesymbolcontainer": {
+ "source": "iana"
+ },
+ "application/slate": {
+ "source": "iana"
+ },
+ "application/smil": {
+ "source": "iana"
+ },
+ "application/smil+xml": {
+ "source": "iana",
+ "extensions": ["smi","smil"]
+ },
+ "application/smpte336m": {
+ "source": "iana"
+ },
+ "application/soap+fastinfoset": {
+ "source": "iana"
+ },
+ "application/soap+xml": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/sparql-query": {
+ "source": "iana",
+ "extensions": ["rq"]
+ },
+ "application/sparql-results+xml": {
+ "source": "iana",
+ "extensions": ["srx"]
+ },
+ "application/spirits-event+xml": {
+ "source": "iana"
+ },
+ "application/sql": {
+ "source": "iana"
+ },
+ "application/srgs": {
+ "source": "iana",
+ "extensions": ["gram"]
+ },
+ "application/srgs+xml": {
+ "source": "iana",
+ "extensions": ["grxml"]
+ },
+ "application/sru+xml": {
+ "source": "iana",
+ "extensions": ["sru"]
+ },
+ "application/ssdl+xml": {
+ "source": "apache",
+ "extensions": ["ssdl"]
+ },
+ "application/ssml+xml": {
+ "source": "iana",
+ "extensions": ["ssml"]
+ },
+ "application/tamp-apex-update": {
+ "source": "iana"
+ },
+ "application/tamp-apex-update-confirm": {
+ "source": "iana"
+ },
+ "application/tamp-community-update": {
+ "source": "iana"
+ },
+ "application/tamp-community-update-confirm": {
+ "source": "iana"
+ },
+ "application/tamp-error": {
+ "source": "iana"
+ },
+ "application/tamp-sequence-adjust": {
+ "source": "iana"
+ },
+ "application/tamp-sequence-adjust-confirm": {
+ "source": "iana"
+ },
+ "application/tamp-status-query": {
+ "source": "iana"
+ },
+ "application/tamp-status-response": {
+ "source": "iana"
+ },
+ "application/tamp-update": {
+ "source": "iana"
+ },
+ "application/tamp-update-confirm": {
+ "source": "iana"
+ },
+ "application/tar": {
+ "compressible": true
+ },
+ "application/tei+xml": {
+ "source": "iana",
+ "extensions": ["tei","teicorpus"]
+ },
+ "application/thraud+xml": {
+ "source": "iana",
+ "extensions": ["tfi"]
+ },
+ "application/timestamp-query": {
+ "source": "iana"
+ },
+ "application/timestamp-reply": {
+ "source": "iana"
+ },
+ "application/timestamped-data": {
+ "source": "iana",
+ "extensions": ["tsd"]
+ },
+ "application/ttml+xml": {
+ "source": "iana"
+ },
+ "application/tve-trigger": {
+ "source": "iana"
+ },
+ "application/ulpfec": {
+ "source": "iana"
+ },
+ "application/urc-grpsheet+xml": {
+ "source": "iana"
+ },
+ "application/urc-ressheet+xml": {
+ "source": "iana"
+ },
+ "application/urc-targetdesc+xml": {
+ "source": "iana"
+ },
+ "application/urc-uisocketdesc+xml": {
+ "source": "iana"
+ },
+ "application/vcard+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vcard+xml": {
+ "source": "iana"
+ },
+ "application/vemmi": {
+ "source": "iana"
+ },
+ "application/vividence.scriptfile": {
+ "source": "apache"
+ },
+ "application/vnd.3gpp.bsf+xml": {
+ "source": "iana"
+ },
+ "application/vnd.3gpp.pic-bw-large": {
+ "source": "iana",
+ "extensions": ["plb"]
+ },
+ "application/vnd.3gpp.pic-bw-small": {
+ "source": "iana",
+ "extensions": ["psb"]
+ },
+ "application/vnd.3gpp.pic-bw-var": {
+ "source": "iana",
+ "extensions": ["pvb"]
+ },
+ "application/vnd.3gpp.sms": {
+ "source": "iana"
+ },
+ "application/vnd.3gpp2.bcmcsinfo+xml": {
+ "source": "iana"
+ },
+ "application/vnd.3gpp2.sms": {
+ "source": "iana"
+ },
+ "application/vnd.3gpp2.tcap": {
+ "source": "iana",
+ "extensions": ["tcap"]
+ },
+ "application/vnd.3m.post-it-notes": {
+ "source": "iana",
+ "extensions": ["pwn"]
+ },
+ "application/vnd.accpac.simply.aso": {
+ "source": "iana",
+ "extensions": ["aso"]
+ },
+ "application/vnd.accpac.simply.imp": {
+ "source": "iana",
+ "extensions": ["imp"]
+ },
+ "application/vnd.acucobol": {
+ "source": "iana",
+ "extensions": ["acu"]
+ },
+ "application/vnd.acucorp": {
+ "source": "iana",
+ "extensions": ["atc","acutc"]
+ },
+ "application/vnd.adobe.air-application-installer-package+zip": {
+ "source": "apache",
+ "extensions": ["air"]
+ },
+ "application/vnd.adobe.flash.movie": {
+ "source": "iana"
+ },
+ "application/vnd.adobe.formscentral.fcdt": {
+ "source": "iana",
+ "extensions": ["fcdt"]
+ },
+ "application/vnd.adobe.fxp": {
+ "source": "iana",
+ "extensions": ["fxp","fxpl"]
+ },
+ "application/vnd.adobe.partial-upload": {
+ "source": "iana"
+ },
+ "application/vnd.adobe.xdp+xml": {
+ "source": "iana",
+ "extensions": ["xdp"]
+ },
+ "application/vnd.adobe.xfdf": {
+ "source": "iana",
+ "extensions": ["xfdf"]
+ },
+ "application/vnd.aether.imp": {
+ "source": "iana"
+ },
+ "application/vnd.ah-barcode": {
+ "source": "iana"
+ },
+ "application/vnd.ahead.space": {
+ "source": "iana",
+ "extensions": ["ahead"]
+ },
+ "application/vnd.airzip.filesecure.azf": {
+ "source": "iana",
+ "extensions": ["azf"]
+ },
+ "application/vnd.airzip.filesecure.azs": {
+ "source": "iana",
+ "extensions": ["azs"]
+ },
+ "application/vnd.amazon.ebook": {
+ "source": "apache",
+ "extensions": ["azw"]
+ },
+ "application/vnd.americandynamics.acc": {
+ "source": "iana",
+ "extensions": ["acc"]
+ },
+ "application/vnd.amiga.ami": {
+ "source": "iana",
+ "extensions": ["ami"]
+ },
+ "application/vnd.amundsen.maze+xml": {
+ "source": "iana"
+ },
+ "application/vnd.android.package-archive": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["apk"]
+ },
+ "application/vnd.anser-web-certificate-issue-initiation": {
+ "source": "iana",
+ "extensions": ["cii"]
+ },
+ "application/vnd.anser-web-funds-transfer-initiation": {
+ "source": "apache",
+ "extensions": ["fti"]
+ },
+ "application/vnd.antix.game-component": {
+ "source": "iana",
+ "extensions": ["atx"]
+ },
+ "application/vnd.apache.thrift.binary": {
+ "source": "iana"
+ },
+ "application/vnd.apache.thrift.compact": {
+ "source": "iana"
+ },
+ "application/vnd.apache.thrift.json": {
+ "source": "iana"
+ },
+ "application/vnd.api+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.apple.installer+xml": {
+ "source": "iana",
+ "extensions": ["mpkg"]
+ },
+ "application/vnd.apple.mpegurl": {
+ "source": "iana",
+ "extensions": ["m3u8"]
+ },
+ "application/vnd.arastra.swi": {
+ "source": "iana"
+ },
+ "application/vnd.aristanetworks.swi": {
+ "source": "iana",
+ "extensions": ["swi"]
+ },
+ "application/vnd.artsquare": {
+ "source": "iana"
+ },
+ "application/vnd.astraea-software.iota": {
+ "source": "iana",
+ "extensions": ["iota"]
+ },
+ "application/vnd.audiograph": {
+ "source": "iana",
+ "extensions": ["aep"]
+ },
+ "application/vnd.autopackage": {
+ "source": "iana"
+ },
+ "application/vnd.avistar+xml": {
+ "source": "iana"
+ },
+ "application/vnd.balsamiq.bmml+xml": {
+ "source": "iana"
+ },
+ "application/vnd.balsamiq.bmpr": {
+ "source": "iana"
+ },
+ "application/vnd.bekitzur-stech+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.blueice.multipass": {
+ "source": "iana",
+ "extensions": ["mpm"]
+ },
+ "application/vnd.bluetooth.ep.oob": {
+ "source": "iana"
+ },
+ "application/vnd.bluetooth.le.oob": {
+ "source": "iana"
+ },
+ "application/vnd.bmi": {
+ "source": "iana",
+ "extensions": ["bmi"]
+ },
+ "application/vnd.businessobjects": {
+ "source": "iana",
+ "extensions": ["rep"]
+ },
+ "application/vnd.cab-jscript": {
+ "source": "iana"
+ },
+ "application/vnd.canon-cpdl": {
+ "source": "iana"
+ },
+ "application/vnd.canon-lips": {
+ "source": "iana"
+ },
+ "application/vnd.cendio.thinlinc.clientconf": {
+ "source": "iana"
+ },
+ "application/vnd.century-systems.tcp_stream": {
+ "source": "iana"
+ },
+ "application/vnd.chemdraw+xml": {
+ "source": "iana",
+ "extensions": ["cdxml"]
+ },
+ "application/vnd.chipnuts.karaoke-mmd": {
+ "source": "iana",
+ "extensions": ["mmd"]
+ },
+ "application/vnd.cinderella": {
+ "source": "iana",
+ "extensions": ["cdy"]
+ },
+ "application/vnd.cirpack.isdn-ext": {
+ "source": "iana"
+ },
+ "application/vnd.citationstyles.style+xml": {
+ "source": "iana"
+ },
+ "application/vnd.claymore": {
+ "source": "iana",
+ "extensions": ["cla"]
+ },
+ "application/vnd.cloanto.rp9": {
+ "source": "iana",
+ "extensions": ["rp9"]
+ },
+ "application/vnd.clonk.c4group": {
+ "source": "iana",
+ "extensions": ["c4g","c4d","c4f","c4p","c4u"]
+ },
+ "application/vnd.cluetrust.cartomobile-config": {
+ "source": "iana",
+ "extensions": ["c11amc"]
+ },
+ "application/vnd.cluetrust.cartomobile-config-pkg": {
+ "source": "iana",
+ "extensions": ["c11amz"]
+ },
+ "application/vnd.coffeescript": {
+ "source": "iana"
+ },
+ "application/vnd.collection+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.collection.doc+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.collection.next+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.commerce-battelle": {
+ "source": "iana"
+ },
+ "application/vnd.commonspace": {
+ "source": "iana",
+ "extensions": ["csp"]
+ },
+ "application/vnd.contact.cmsg": {
+ "source": "iana",
+ "extensions": ["cdbcmsg"]
+ },
+ "application/vnd.cosmocaller": {
+ "source": "iana",
+ "extensions": ["cmc"]
+ },
+ "application/vnd.crick.clicker": {
+ "source": "iana",
+ "extensions": ["clkx"]
+ },
+ "application/vnd.crick.clicker.keyboard": {
+ "source": "iana",
+ "extensions": ["clkk"]
+ },
+ "application/vnd.crick.clicker.palette": {
+ "source": "iana",
+ "extensions": ["clkp"]
+ },
+ "application/vnd.crick.clicker.template": {
+ "source": "iana",
+ "extensions": ["clkt"]
+ },
+ "application/vnd.crick.clicker.wordbank": {
+ "source": "iana",
+ "extensions": ["clkw"]
+ },
+ "application/vnd.criticaltools.wbs+xml": {
+ "source": "iana",
+ "extensions": ["wbs"]
+ },
+ "application/vnd.ctc-posml": {
+ "source": "iana",
+ "extensions": ["pml"]
+ },
+ "application/vnd.ctct.ws+xml": {
+ "source": "iana"
+ },
+ "application/vnd.cups-pdf": {
+ "source": "iana"
+ },
+ "application/vnd.cups-postscript": {
+ "source": "iana"
+ },
+ "application/vnd.cups-ppd": {
+ "source": "iana",
+ "extensions": ["ppd"]
+ },
+ "application/vnd.cups-raster": {
+ "source": "iana"
+ },
+ "application/vnd.cups-raw": {
+ "source": "iana"
+ },
+ "application/vnd.curl": {
+ "source": "iana"
+ },
+ "application/vnd.curl.car": {
+ "source": "apache",
+ "extensions": ["car"]
+ },
+ "application/vnd.curl.pcurl": {
+ "source": "apache",
+ "extensions": ["pcurl"]
+ },
+ "application/vnd.cyan.dean.root+xml": {
+ "source": "iana"
+ },
+ "application/vnd.cybank": {
+ "source": "iana"
+ },
+ "application/vnd.dart": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["dart"]
+ },
+ "application/vnd.data-vision.rdz": {
+ "source": "iana",
+ "extensions": ["rdz"]
+ },
+ "application/vnd.debian.binary-package": {
+ "source": "iana"
+ },
+ "application/vnd.dece.data": {
+ "source": "iana",
+ "extensions": ["uvf","uvvf","uvd","uvvd"]
+ },
+ "application/vnd.dece.ttml+xml": {
+ "source": "iana",
+ "extensions": ["uvt","uvvt"]
+ },
+ "application/vnd.dece.unspecified": {
+ "source": "iana",
+ "extensions": ["uvx","uvvx"]
+ },
+ "application/vnd.dece.zip": {
+ "source": "iana",
+ "extensions": ["uvz","uvvz"]
+ },
+ "application/vnd.denovo.fcselayout-link": {
+ "source": "iana",
+ "extensions": ["fe_launch"]
+ },
+ "application/vnd.desmume-movie": {
+ "source": "iana"
+ },
+ "application/vnd.dir-bi.plate-dl-nosuffix": {
+ "source": "iana"
+ },
+ "application/vnd.dm.delegation+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dna": {
+ "source": "iana",
+ "extensions": ["dna"]
+ },
+ "application/vnd.document+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.dolby.mlp": {
+ "source": "apache",
+ "extensions": ["mlp"]
+ },
+ "application/vnd.dolby.mobile.1": {
+ "source": "iana"
+ },
+ "application/vnd.dolby.mobile.2": {
+ "source": "iana"
+ },
+ "application/vnd.doremir.scorecloud-binary-document": {
+ "source": "iana"
+ },
+ "application/vnd.dpgraph": {
+ "source": "iana",
+ "extensions": ["dpg"]
+ },
+ "application/vnd.dreamfactory": {
+ "source": "iana",
+ "extensions": ["dfac"]
+ },
+ "application/vnd.ds-keypoint": {
+ "source": "apache",
+ "extensions": ["kpxx"]
+ },
+ "application/vnd.dtg.local": {
+ "source": "iana"
+ },
+ "application/vnd.dtg.local.flash": {
+ "source": "iana"
+ },
+ "application/vnd.dtg.local.html": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.ait": {
+ "source": "iana",
+ "extensions": ["ait"]
+ },
+ "application/vnd.dvb.dvbj": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.esgcontainer": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.ipdcdftnotifaccess": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.ipdcesgaccess": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.ipdcesgaccess2": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.ipdcesgpdd": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.ipdcroaming": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.iptv.alfec-base": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.iptv.alfec-enhancement": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-aggregate-root+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-container+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-generic+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-ia-msglist+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-ia-registration-request+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-ia-registration-response+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.notif-init+xml": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.pfr": {
+ "source": "iana"
+ },
+ "application/vnd.dvb.service": {
+ "source": "iana",
+ "extensions": ["svc"]
+ },
+ "application/vnd.dxr": {
+ "source": "iana"
+ },
+ "application/vnd.dynageo": {
+ "source": "iana",
+ "extensions": ["geo"]
+ },
+ "application/vnd.dzr": {
+ "source": "iana"
+ },
+ "application/vnd.easykaraoke.cdgdownload": {
+ "source": "iana"
+ },
+ "application/vnd.ecdis-update": {
+ "source": "iana"
+ },
+ "application/vnd.ecowin.chart": {
+ "source": "iana",
+ "extensions": ["mag"]
+ },
+ "application/vnd.ecowin.filerequest": {
+ "source": "iana"
+ },
+ "application/vnd.ecowin.fileupdate": {
+ "source": "iana"
+ },
+ "application/vnd.ecowin.series": {
+ "source": "iana"
+ },
+ "application/vnd.ecowin.seriesrequest": {
+ "source": "iana"
+ },
+ "application/vnd.ecowin.seriesupdate": {
+ "source": "iana"
+ },
+ "application/vnd.emclient.accessrequest+xml": {
+ "source": "iana"
+ },
+ "application/vnd.enliven": {
+ "source": "iana",
+ "extensions": ["nml"]
+ },
+ "application/vnd.enphase.envoy": {
+ "source": "iana"
+ },
+ "application/vnd.eprints.data+xml": {
+ "source": "iana"
+ },
+ "application/vnd.epson.esf": {
+ "source": "iana",
+ "extensions": ["esf"]
+ },
+ "application/vnd.epson.msf": {
+ "source": "iana",
+ "extensions": ["msf"]
+ },
+ "application/vnd.epson.quickanime": {
+ "source": "iana",
+ "extensions": ["qam"]
+ },
+ "application/vnd.epson.salt": {
+ "source": "iana",
+ "extensions": ["slt"]
+ },
+ "application/vnd.epson.ssf": {
+ "source": "iana",
+ "extensions": ["ssf"]
+ },
+ "application/vnd.ericsson.quickcall": {
+ "source": "iana"
+ },
+ "application/vnd.eszigno3+xml": {
+ "source": "iana",
+ "extensions": ["es3","et3"]
+ },
+ "application/vnd.etsi.aoc+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.asic-e+zip": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.asic-s+zip": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.cug+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvcommand+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvdiscovery+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvprofile+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvsad-bc+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvsad-cod+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvsad-npvr+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvservice+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvsync+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.iptvueprofile+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.mcid+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.mheg5": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.overload-control-policy-dataset+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.pstn+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.sci+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.simservs+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.timestamp-token": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.tsl+xml": {
+ "source": "iana"
+ },
+ "application/vnd.etsi.tsl.der": {
+ "source": "iana"
+ },
+ "application/vnd.eudora.data": {
+ "source": "iana"
+ },
+ "application/vnd.ezpix-album": {
+ "source": "iana",
+ "extensions": ["ez2"]
+ },
+ "application/vnd.ezpix-package": {
+ "source": "iana",
+ "extensions": ["ez3"]
+ },
+ "application/vnd.f-secure.mobile": {
+ "source": "iana"
+ },
+ "application/vnd.fastcopy-disk-image": {
+ "source": "iana"
+ },
+ "application/vnd.fdf": {
+ "source": "iana",
+ "extensions": ["fdf"]
+ },
+ "application/vnd.fdsn.mseed": {
+ "source": "iana",
+ "extensions": ["mseed"]
+ },
+ "application/vnd.fdsn.seed": {
+ "source": "iana",
+ "extensions": ["seed","dataless"]
+ },
+ "application/vnd.ffsns": {
+ "source": "iana"
+ },
+ "application/vnd.fints": {
+ "source": "iana"
+ },
+ "application/vnd.flographit": {
+ "source": "iana",
+ "extensions": ["gph"]
+ },
+ "application/vnd.fluxtime.clip": {
+ "source": "iana",
+ "extensions": ["ftc"]
+ },
+ "application/vnd.font-fontforge-sfd": {
+ "source": "iana"
+ },
+ "application/vnd.framemaker": {
+ "source": "iana",
+ "extensions": ["fm","frame","maker","book"]
+ },
+ "application/vnd.frogans.fnc": {
+ "source": "iana",
+ "extensions": ["fnc"]
+ },
+ "application/vnd.frogans.ltf": {
+ "source": "iana",
+ "extensions": ["ltf"]
+ },
+ "application/vnd.fsc.weblaunch": {
+ "source": "iana",
+ "extensions": ["fsc"]
+ },
+ "application/vnd.fujitsu.oasys": {
+ "source": "iana",
+ "extensions": ["oas"]
+ },
+ "application/vnd.fujitsu.oasys2": {
+ "source": "iana",
+ "extensions": ["oa2"]
+ },
+ "application/vnd.fujitsu.oasys3": {
+ "source": "iana",
+ "extensions": ["oa3"]
+ },
+ "application/vnd.fujitsu.oasysgp": {
+ "source": "iana",
+ "extensions": ["fg5"]
+ },
+ "application/vnd.fujitsu.oasysprs": {
+ "source": "iana",
+ "extensions": ["bh2"]
+ },
+ "application/vnd.fujixerox.art-ex": {
+ "source": "iana"
+ },
+ "application/vnd.fujixerox.art4": {
+ "source": "iana"
+ },
+ "application/vnd.fujixerox.ddd": {
+ "source": "iana",
+ "extensions": ["ddd"]
+ },
+ "application/vnd.fujixerox.docuworks": {
+ "source": "iana",
+ "extensions": ["xdw"]
+ },
+ "application/vnd.fujixerox.docuworks.binder": {
+ "source": "iana",
+ "extensions": ["xbd"]
+ },
+ "application/vnd.fujixerox.docuworks.container": {
+ "source": "iana"
+ },
+ "application/vnd.fujixerox.hbpl": {
+ "source": "iana"
+ },
+ "application/vnd.fut-misnet": {
+ "source": "iana"
+ },
+ "application/vnd.fuzzysheet": {
+ "source": "iana",
+ "extensions": ["fzs"]
+ },
+ "application/vnd.genomatix.tuxedo": {
+ "source": "iana",
+ "extensions": ["txd"]
+ },
+ "application/vnd.geo+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.geocube+xml": {
+ "source": "iana"
+ },
+ "application/vnd.geogebra.file": {
+ "source": "iana",
+ "extensions": ["ggb"]
+ },
+ "application/vnd.geogebra.tool": {
+ "source": "iana",
+ "extensions": ["ggt"]
+ },
+ "application/vnd.geometry-explorer": {
+ "source": "iana",
+ "extensions": ["gex","gre"]
+ },
+ "application/vnd.geonext": {
+ "source": "iana",
+ "extensions": ["gxt"]
+ },
+ "application/vnd.geoplan": {
+ "source": "iana",
+ "extensions": ["g2w"]
+ },
+ "application/vnd.geospace": {
+ "source": "iana",
+ "extensions": ["g3w"]
+ },
+ "application/vnd.gerber": {
+ "source": "iana"
+ },
+ "application/vnd.globalplatform.card-content-mgt": {
+ "source": "iana"
+ },
+ "application/vnd.globalplatform.card-content-mgt-response": {
+ "source": "iana"
+ },
+ "application/vnd.gmx": {
+ "source": "iana",
+ "extensions": ["gmx"]
+ },
+ "application/vnd.google-earth.kml+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["kml"]
+ },
+ "application/vnd.google-earth.kmz": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["kmz"]
+ },
+ "application/vnd.gov.sk.e-form+xml": {
+ "source": "iana"
+ },
+ "application/vnd.gov.sk.e-form+zip": {
+ "source": "iana"
+ },
+ "application/vnd.gov.sk.xmldatacontainer+xml": {
+ "source": "iana"
+ },
+ "application/vnd.grafeq": {
+ "source": "iana",
+ "extensions": ["gqf","gqs"]
+ },
+ "application/vnd.gridmp": {
+ "source": "iana"
+ },
+ "application/vnd.groove-account": {
+ "source": "iana",
+ "extensions": ["gac"]
+ },
+ "application/vnd.groove-help": {
+ "source": "iana",
+ "extensions": ["ghf"]
+ },
+ "application/vnd.groove-identity-message": {
+ "source": "iana",
+ "extensions": ["gim"]
+ },
+ "application/vnd.groove-injector": {
+ "source": "iana",
+ "extensions": ["grv"]
+ },
+ "application/vnd.groove-tool-message": {
+ "source": "iana",
+ "extensions": ["gtm"]
+ },
+ "application/vnd.groove-tool-template": {
+ "source": "iana",
+ "extensions": ["tpl"]
+ },
+ "application/vnd.groove-vcard": {
+ "source": "iana",
+ "extensions": ["vcg"]
+ },
+ "application/vnd.hal+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.hal+xml": {
+ "source": "iana",
+ "extensions": ["hal"]
+ },
+ "application/vnd.handheld-entertainment+xml": {
+ "source": "iana",
+ "extensions": ["zmm"]
+ },
+ "application/vnd.hbci": {
+ "source": "iana",
+ "extensions": ["hbci"]
+ },
+ "application/vnd.hcl-bireports": {
+ "source": "iana"
+ },
+ "application/vnd.heroku+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.hhe.lesson-player": {
+ "source": "iana",
+ "extensions": ["les"]
+ },
+ "application/vnd.hp-hpgl": {
+ "source": "iana",
+ "extensions": ["hpgl"]
+ },
+ "application/vnd.hp-hpid": {
+ "source": "iana",
+ "extensions": ["hpid"]
+ },
+ "application/vnd.hp-hps": {
+ "source": "iana",
+ "extensions": ["hps"]
+ },
+ "application/vnd.hp-jlyt": {
+ "source": "iana",
+ "extensions": ["jlt"]
+ },
+ "application/vnd.hp-pcl": {
+ "source": "iana",
+ "extensions": ["pcl"]
+ },
+ "application/vnd.hp-pclxl": {
+ "source": "iana",
+ "extensions": ["pclxl"]
+ },
+ "application/vnd.httphone": {
+ "source": "iana"
+ },
+ "application/vnd.hydrostatix.sof-data": {
+ "source": "iana"
+ },
+ "application/vnd.hzn-3d-crossword": {
+ "source": "iana"
+ },
+ "application/vnd.ibm.afplinedata": {
+ "source": "iana"
+ },
+ "application/vnd.ibm.electronic-media": {
+ "source": "iana"
+ },
+ "application/vnd.ibm.minipay": {
+ "source": "iana",
+ "extensions": ["mpy"]
+ },
+ "application/vnd.ibm.modcap": {
+ "source": "iana",
+ "extensions": ["afp","listafp","list3820"]
+ },
+ "application/vnd.ibm.rights-management": {
+ "source": "iana",
+ "extensions": ["irm"]
+ },
+ "application/vnd.ibm.secure-container": {
+ "source": "iana",
+ "extensions": ["sc"]
+ },
+ "application/vnd.iccprofile": {
+ "source": "iana",
+ "extensions": ["icc","icm"]
+ },
+ "application/vnd.ieee.1905": {
+ "source": "iana"
+ },
+ "application/vnd.igloader": {
+ "source": "iana",
+ "extensions": ["igl"]
+ },
+ "application/vnd.immervision-ivp": {
+ "source": "iana",
+ "extensions": ["ivp"]
+ },
+ "application/vnd.immervision-ivu": {
+ "source": "iana",
+ "extensions": ["ivu"]
+ },
+ "application/vnd.ims.imsccv1p1": {
+ "source": "iana"
+ },
+ "application/vnd.ims.imsccv1p2": {
+ "source": "iana"
+ },
+ "application/vnd.ims.imsccv1p3": {
+ "source": "iana"
+ },
+ "application/vnd.ims.lis.v2.result+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.ims.lti.v2.toolconsumerprofile+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.ims.lti.v2.toolproxy+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.ims.lti.v2.toolproxy.id+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.ims.lti.v2.toolsettings+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.ims.lti.v2.toolsettings.simple+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.informedcontrol.rms+xml": {
+ "source": "iana"
+ },
+ "application/vnd.informix-visionary": {
+ "source": "iana"
+ },
+ "application/vnd.infotech.project": {
+ "source": "iana"
+ },
+ "application/vnd.infotech.project+xml": {
+ "source": "iana"
+ },
+ "application/vnd.innopath.wamp.notification": {
+ "source": "iana"
+ },
+ "application/vnd.insors.igm": {
+ "source": "iana",
+ "extensions": ["igm"]
+ },
+ "application/vnd.intercon.formnet": {
+ "source": "iana",
+ "extensions": ["xpw","xpx"]
+ },
+ "application/vnd.intergeo": {
+ "source": "iana",
+ "extensions": ["i2g"]
+ },
+ "application/vnd.intertrust.digibox": {
+ "source": "iana"
+ },
+ "application/vnd.intertrust.nncp": {
+ "source": "iana"
+ },
+ "application/vnd.intu.qbo": {
+ "source": "iana",
+ "extensions": ["qbo"]
+ },
+ "application/vnd.intu.qfx": {
+ "source": "iana",
+ "extensions": ["qfx"]
+ },
+ "application/vnd.iptc.g2.catalogitem+xml": {
+ "source": "iana"
+ },
+ "application/vnd.iptc.g2.conceptitem+xml": {
+ "source": "iana"
+ },
+ "application/vnd.iptc.g2.knowledgeitem+xml": {
+ "source": "iana"
+ },
+ "application/vnd.iptc.g2.newsitem+xml": {
+ "source": "iana"
+ },
+ "application/vnd.iptc.g2.newsmessage+xml": {
+ "source": "iana"
+ },
+ "application/vnd.iptc.g2.packageitem+xml": {
+ "source": "iana"
+ },
+ "application/vnd.iptc.g2.planningitem+xml": {
+ "source": "iana"
+ },
+ "application/vnd.ipunplugged.rcprofile": {
+ "source": "iana",
+ "extensions": ["rcprofile"]
+ },
+ "application/vnd.irepository.package+xml": {
+ "source": "iana",
+ "extensions": ["irp"]
+ },
+ "application/vnd.is-xpr": {
+ "source": "iana",
+ "extensions": ["xpr"]
+ },
+ "application/vnd.isac.fcs": {
+ "source": "iana",
+ "extensions": ["fcs"]
+ },
+ "application/vnd.jam": {
+ "source": "iana",
+ "extensions": ["jam"]
+ },
+ "application/vnd.japannet-directory-service": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-jpnstore-wakeup": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-payment-wakeup": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-registration": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-registration-wakeup": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-setstore-wakeup": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-verification": {
+ "source": "iana"
+ },
+ "application/vnd.japannet-verification-wakeup": {
+ "source": "iana"
+ },
+ "application/vnd.jcp.javame.midlet-rms": {
+ "source": "iana",
+ "extensions": ["rms"]
+ },
+ "application/vnd.jisp": {
+ "source": "iana",
+ "extensions": ["jisp"]
+ },
+ "application/vnd.joost.joda-archive": {
+ "source": "iana",
+ "extensions": ["joda"]
+ },
+ "application/vnd.jsk.isdn-ngn": {
+ "source": "iana"
+ },
+ "application/vnd.kahootz": {
+ "source": "iana",
+ "extensions": ["ktz","ktr"]
+ },
+ "application/vnd.kde.karbon": {
+ "source": "iana",
+ "extensions": ["karbon"]
+ },
+ "application/vnd.kde.kchart": {
+ "source": "iana",
+ "extensions": ["chrt"]
+ },
+ "application/vnd.kde.kformula": {
+ "source": "iana",
+ "extensions": ["kfo"]
+ },
+ "application/vnd.kde.kivio": {
+ "source": "iana",
+ "extensions": ["flw"]
+ },
+ "application/vnd.kde.kontour": {
+ "source": "iana",
+ "extensions": ["kon"]
+ },
+ "application/vnd.kde.kpresenter": {
+ "source": "iana",
+ "extensions": ["kpr","kpt"]
+ },
+ "application/vnd.kde.kspread": {
+ "source": "iana",
+ "extensions": ["ksp"]
+ },
+ "application/vnd.kde.kword": {
+ "source": "iana",
+ "extensions": ["kwd","kwt"]
+ },
+ "application/vnd.kenameaapp": {
+ "source": "iana",
+ "extensions": ["htke"]
+ },
+ "application/vnd.kidspiration": {
+ "source": "iana",
+ "extensions": ["kia"]
+ },
+ "application/vnd.kinar": {
+ "source": "iana",
+ "extensions": ["kne","knp"]
+ },
+ "application/vnd.koan": {
+ "source": "iana",
+ "extensions": ["skp","skd","skt","skm"]
+ },
+ "application/vnd.kodak-descriptor": {
+ "source": "iana",
+ "extensions": ["sse"]
+ },
+ "application/vnd.las.las+xml": {
+ "source": "iana",
+ "extensions": ["lasxml"]
+ },
+ "application/vnd.liberty-request+xml": {
+ "source": "iana"
+ },
+ "application/vnd.llamagraphics.life-balance.desktop": {
+ "source": "iana",
+ "extensions": ["lbd"]
+ },
+ "application/vnd.llamagraphics.life-balance.exchange+xml": {
+ "source": "iana",
+ "extensions": ["lbe"]
+ },
+ "application/vnd.lotus-1-2-3": {
+ "source": "iana",
+ "extensions": ["123"]
+ },
+ "application/vnd.lotus-approach": {
+ "source": "iana",
+ "extensions": ["apr"]
+ },
+ "application/vnd.lotus-freelance": {
+ "source": "iana",
+ "extensions": ["pre"]
+ },
+ "application/vnd.lotus-notes": {
+ "source": "iana",
+ "extensions": ["nsf"]
+ },
+ "application/vnd.lotus-organizer": {
+ "source": "iana",
+ "extensions": ["org"]
+ },
+ "application/vnd.lotus-screencam": {
+ "source": "iana",
+ "extensions": ["scm"]
+ },
+ "application/vnd.lotus-wordpro": {
+ "source": "iana",
+ "extensions": ["lwp"]
+ },
+ "application/vnd.macports.portpkg": {
+ "source": "iana",
+ "extensions": ["portpkg"]
+ },
+ "application/vnd.marlin.drm.actiontoken+xml": {
+ "source": "iana"
+ },
+ "application/vnd.marlin.drm.conftoken+xml": {
+ "source": "iana"
+ },
+ "application/vnd.marlin.drm.license+xml": {
+ "source": "iana"
+ },
+ "application/vnd.marlin.drm.mdcf": {
+ "source": "iana"
+ },
+ "application/vnd.mason+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.maxmind.maxmind-db": {
+ "source": "iana"
+ },
+ "application/vnd.mcd": {
+ "source": "iana",
+ "extensions": ["mcd"]
+ },
+ "application/vnd.medcalcdata": {
+ "source": "iana",
+ "extensions": ["mc1"]
+ },
+ "application/vnd.mediastation.cdkey": {
+ "source": "iana",
+ "extensions": ["cdkey"]
+ },
+ "application/vnd.meridian-slingshot": {
+ "source": "iana"
+ },
+ "application/vnd.mfer": {
+ "source": "iana",
+ "extensions": ["mwf"]
+ },
+ "application/vnd.mfmp": {
+ "source": "iana",
+ "extensions": ["mfm"]
+ },
+ "application/vnd.micro+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.micrografx.flo": {
+ "source": "iana",
+ "extensions": ["flo"]
+ },
+ "application/vnd.micrografx.igx": {
+ "source": "iana",
+ "extensions": ["igx"]
+ },
+ "application/vnd.microsoft.portable-executable": {
+ "source": "iana"
+ },
+ "application/vnd.miele+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.mif": {
+ "source": "iana",
+ "extensions": ["mif"]
+ },
+ "application/vnd.minisoft-hp3000-save": {
+ "source": "iana"
+ },
+ "application/vnd.mitsubishi.misty-guard.trustweb": {
+ "source": "iana"
+ },
+ "application/vnd.mobius.daf": {
+ "source": "iana",
+ "extensions": ["daf"]
+ },
+ "application/vnd.mobius.dis": {
+ "source": "iana",
+ "extensions": ["dis"]
+ },
+ "application/vnd.mobius.mbk": {
+ "source": "iana",
+ "extensions": ["mbk"]
+ },
+ "application/vnd.mobius.mqy": {
+ "source": "iana",
+ "extensions": ["mqy"]
+ },
+ "application/vnd.mobius.msl": {
+ "source": "iana",
+ "extensions": ["msl"]
+ },
+ "application/vnd.mobius.plc": {
+ "source": "iana",
+ "extensions": ["plc"]
+ },
+ "application/vnd.mobius.txf": {
+ "source": "iana",
+ "extensions": ["txf"]
+ },
+ "application/vnd.mophun.application": {
+ "source": "iana",
+ "extensions": ["mpn"]
+ },
+ "application/vnd.mophun.certificate": {
+ "source": "iana",
+ "extensions": ["mpc"]
+ },
+ "application/vnd.motorola.flexsuite": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.flexsuite.adsi": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.flexsuite.fis": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.flexsuite.gotap": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.flexsuite.kmr": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.flexsuite.ttc": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.flexsuite.wem": {
+ "source": "iana"
+ },
+ "application/vnd.motorola.iprm": {
+ "source": "iana"
+ },
+ "application/vnd.mozilla.xul+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["xul"]
+ },
+ "application/vnd.ms-3mfdocument": {
+ "source": "iana"
+ },
+ "application/vnd.ms-artgalry": {
+ "source": "iana",
+ "extensions": ["cil"]
+ },
+ "application/vnd.ms-asf": {
+ "source": "iana"
+ },
+ "application/vnd.ms-cab-compressed": {
+ "source": "iana",
+ "extensions": ["cab"]
+ },
+ "application/vnd.ms-color.iccprofile": {
+ "source": "apache"
+ },
+ "application/vnd.ms-excel": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["xls","xlm","xla","xlc","xlt","xlw"]
+ },
+ "application/vnd.ms-excel.addin.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["xlam"]
+ },
+ "application/vnd.ms-excel.sheet.binary.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["xlsb"]
+ },
+ "application/vnd.ms-excel.sheet.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["xlsm"]
+ },
+ "application/vnd.ms-excel.template.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["xltm"]
+ },
+ "application/vnd.ms-fontobject": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["eot"]
+ },
+ "application/vnd.ms-htmlhelp": {
+ "source": "iana",
+ "extensions": ["chm"]
+ },
+ "application/vnd.ms-ims": {
+ "source": "iana",
+ "extensions": ["ims"]
+ },
+ "application/vnd.ms-lrm": {
+ "source": "iana",
+ "extensions": ["lrm"]
+ },
+ "application/vnd.ms-office.activex+xml": {
+ "source": "iana"
+ },
+ "application/vnd.ms-officetheme": {
+ "source": "iana",
+ "extensions": ["thmx"]
+ },
+ "application/vnd.ms-opentype": {
+ "source": "apache",
+ "compressible": true
+ },
+ "application/vnd.ms-package.obfuscated-opentype": {
+ "source": "apache"
+ },
+ "application/vnd.ms-pki.seccat": {
+ "source": "apache",
+ "extensions": ["cat"]
+ },
+ "application/vnd.ms-pki.stl": {
+ "source": "apache",
+ "extensions": ["stl"]
+ },
+ "application/vnd.ms-playready.initiator+xml": {
+ "source": "iana"
+ },
+ "application/vnd.ms-powerpoint": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["ppt","pps","pot"]
+ },
+ "application/vnd.ms-powerpoint.addin.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["ppam"]
+ },
+ "application/vnd.ms-powerpoint.presentation.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["pptm"]
+ },
+ "application/vnd.ms-powerpoint.slide.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["sldm"]
+ },
+ "application/vnd.ms-powerpoint.slideshow.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["ppsm"]
+ },
+ "application/vnd.ms-powerpoint.template.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["potm"]
+ },
+ "application/vnd.ms-printing.printticket+xml": {
+ "source": "apache"
+ },
+ "application/vnd.ms-project": {
+ "source": "iana",
+ "extensions": ["mpp","mpt"]
+ },
+ "application/vnd.ms-tnef": {
+ "source": "iana"
+ },
+ "application/vnd.ms-windows.printerpairing": {
+ "source": "iana"
+ },
+ "application/vnd.ms-wmdrm.lic-chlg-req": {
+ "source": "iana"
+ },
+ "application/vnd.ms-wmdrm.lic-resp": {
+ "source": "iana"
+ },
+ "application/vnd.ms-wmdrm.meter-chlg-req": {
+ "source": "iana"
+ },
+ "application/vnd.ms-wmdrm.meter-resp": {
+ "source": "iana"
+ },
+ "application/vnd.ms-word.document.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["docm"]
+ },
+ "application/vnd.ms-word.template.macroenabled.12": {
+ "source": "iana",
+ "extensions": ["dotm"]
+ },
+ "application/vnd.ms-works": {
+ "source": "iana",
+ "extensions": ["wps","wks","wcm","wdb"]
+ },
+ "application/vnd.ms-wpl": {
+ "source": "iana",
+ "extensions": ["wpl"]
+ },
+ "application/vnd.ms-xpsdocument": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["xps"]
+ },
+ "application/vnd.msa-disk-image": {
+ "source": "iana"
+ },
+ "application/vnd.mseq": {
+ "source": "iana",
+ "extensions": ["mseq"]
+ },
+ "application/vnd.msign": {
+ "source": "iana"
+ },
+ "application/vnd.multiad.creator": {
+ "source": "iana"
+ },
+ "application/vnd.multiad.creator.cif": {
+ "source": "iana"
+ },
+ "application/vnd.music-niff": {
+ "source": "iana"
+ },
+ "application/vnd.musician": {
+ "source": "iana",
+ "extensions": ["mus"]
+ },
+ "application/vnd.muvee.style": {
+ "source": "iana",
+ "extensions": ["msty"]
+ },
+ "application/vnd.mynfc": {
+ "source": "iana",
+ "extensions": ["taglet"]
+ },
+ "application/vnd.ncd.control": {
+ "source": "iana"
+ },
+ "application/vnd.ncd.reference": {
+ "source": "iana"
+ },
+ "application/vnd.nervana": {
+ "source": "iana"
+ },
+ "application/vnd.netfpx": {
+ "source": "iana"
+ },
+ "application/vnd.neurolanguage.nlu": {
+ "source": "iana",
+ "extensions": ["nlu"]
+ },
+ "application/vnd.nintendo.nitro.rom": {
+ "source": "iana"
+ },
+ "application/vnd.nintendo.snes.rom": {
+ "source": "iana"
+ },
+ "application/vnd.nitf": {
+ "source": "iana",
+ "extensions": ["ntf","nitf"]
+ },
+ "application/vnd.noblenet-directory": {
+ "source": "iana",
+ "extensions": ["nnd"]
+ },
+ "application/vnd.noblenet-sealer": {
+ "source": "iana",
+ "extensions": ["nns"]
+ },
+ "application/vnd.noblenet-web": {
+ "source": "iana",
+ "extensions": ["nnw"]
+ },
+ "application/vnd.nokia.catalogs": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.conml+wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.conml+xml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.iptv.config+xml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.isds-radio-presets": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.landmark+wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.landmark+xml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.landmarkcollection+xml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.n-gage.ac+xml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.n-gage.data": {
+ "source": "iana",
+ "extensions": ["ngdat"]
+ },
+ "application/vnd.nokia.n-gage.symbian.install": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.ncd": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.pcd+wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.pcd+xml": {
+ "source": "iana"
+ },
+ "application/vnd.nokia.radio-preset": {
+ "source": "iana",
+ "extensions": ["rpst"]
+ },
+ "application/vnd.nokia.radio-presets": {
+ "source": "iana",
+ "extensions": ["rpss"]
+ },
+ "application/vnd.novadigm.edm": {
+ "source": "iana",
+ "extensions": ["edm"]
+ },
+ "application/vnd.novadigm.edx": {
+ "source": "iana",
+ "extensions": ["edx"]
+ },
+ "application/vnd.novadigm.ext": {
+ "source": "iana",
+ "extensions": ["ext"]
+ },
+ "application/vnd.ntt-local.content-share": {
+ "source": "iana"
+ },
+ "application/vnd.ntt-local.file-transfer": {
+ "source": "iana"
+ },
+ "application/vnd.ntt-local.ogw_remote-access": {
+ "source": "iana"
+ },
+ "application/vnd.ntt-local.sip-ta_remote": {
+ "source": "iana"
+ },
+ "application/vnd.ntt-local.sip-ta_tcp_stream": {
+ "source": "iana"
+ },
+ "application/vnd.oasis.opendocument.chart": {
+ "source": "iana",
+ "extensions": ["odc"]
+ },
+ "application/vnd.oasis.opendocument.chart-template": {
+ "source": "iana",
+ "extensions": ["otc"]
+ },
+ "application/vnd.oasis.opendocument.database": {
+ "source": "iana",
+ "extensions": ["odb"]
+ },
+ "application/vnd.oasis.opendocument.formula": {
+ "source": "iana",
+ "extensions": ["odf"]
+ },
+ "application/vnd.oasis.opendocument.formula-template": {
+ "source": "iana",
+ "extensions": ["odft"]
+ },
+ "application/vnd.oasis.opendocument.graphics": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["odg"]
+ },
+ "application/vnd.oasis.opendocument.graphics-template": {
+ "source": "iana",
+ "extensions": ["otg"]
+ },
+ "application/vnd.oasis.opendocument.image": {
+ "source": "iana",
+ "extensions": ["odi"]
+ },
+ "application/vnd.oasis.opendocument.image-template": {
+ "source": "iana",
+ "extensions": ["oti"]
+ },
+ "application/vnd.oasis.opendocument.presentation": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["odp"]
+ },
+ "application/vnd.oasis.opendocument.presentation-template": {
+ "source": "iana",
+ "extensions": ["otp"]
+ },
+ "application/vnd.oasis.opendocument.spreadsheet": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["ods"]
+ },
+ "application/vnd.oasis.opendocument.spreadsheet-template": {
+ "source": "iana",
+ "extensions": ["ots"]
+ },
+ "application/vnd.oasis.opendocument.text": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["odt"]
+ },
+ "application/vnd.oasis.opendocument.text-master": {
+ "source": "iana",
+ "extensions": ["odm"]
+ },
+ "application/vnd.oasis.opendocument.text-template": {
+ "source": "iana",
+ "extensions": ["ott"]
+ },
+ "application/vnd.oasis.opendocument.text-web": {
+ "source": "iana",
+ "extensions": ["oth"]
+ },
+ "application/vnd.obn": {
+ "source": "iana"
+ },
+ "application/vnd.oftn.l10n+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.oipf.contentaccessdownload+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.contentaccessstreaming+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.cspg-hexbinary": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.dae.svg+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.dae.xhtml+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.mippvcontrolmessage+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.pae.gem": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.spdiscovery+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.spdlist+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.ueprofile+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oipf.userprofile+xml": {
+ "source": "iana"
+ },
+ "application/vnd.olpc-sugar": {
+ "source": "iana",
+ "extensions": ["xo"]
+ },
+ "application/vnd.oma-scws-config": {
+ "source": "iana"
+ },
+ "application/vnd.oma-scws-http-request": {
+ "source": "iana"
+ },
+ "application/vnd.oma-scws-http-response": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.associated-procedure-parameter+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.drm-trigger+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.imd+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.ltkm": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.notification+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.provisioningtrigger": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.sgboot": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.sgdd+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.sgdu": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.simple-symbol-container": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.smartcard-trigger+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.sprov+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.bcast.stkm": {
+ "source": "iana"
+ },
+ "application/vnd.oma.cab-address-book+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.cab-feature-handler+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.cab-pcc+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.cab-subs-invite+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.cab-user-prefs+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.dcd": {
+ "source": "iana"
+ },
+ "application/vnd.oma.dcdc": {
+ "source": "iana"
+ },
+ "application/vnd.oma.dd2+xml": {
+ "source": "iana",
+ "extensions": ["dd2"]
+ },
+ "application/vnd.oma.drm.risd+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.group-usage-list+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.pal+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.poc.detailed-progress-report+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.poc.final-report+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.poc.groups+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.poc.invocation-descriptor+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.poc.optimized-progress-report+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.push": {
+ "source": "iana"
+ },
+ "application/vnd.oma.scidm.messages+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oma.xcap-directory+xml": {
+ "source": "iana"
+ },
+ "application/vnd.omads-email+xml": {
+ "source": "iana"
+ },
+ "application/vnd.omads-file+xml": {
+ "source": "iana"
+ },
+ "application/vnd.omads-folder+xml": {
+ "source": "iana"
+ },
+ "application/vnd.omaloc-supl-init": {
+ "source": "iana"
+ },
+ "application/vnd.openeye.oeb": {
+ "source": "iana"
+ },
+ "application/vnd.openofficeorg.extension": {
+ "source": "apache",
+ "extensions": ["oxt"]
+ },
+ "application/vnd.openxmlformats-officedocument.custom-properties+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.customxmlproperties+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawing+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.extended-properties+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml-template": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.comments+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["pptx"]
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slide": {
+ "source": "iana",
+ "extensions": ["sldx"]
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slide+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow": {
+ "source": "iana",
+ "extensions": ["ppsx"]
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.tags+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.template": {
+ "source": "apache",
+ "extensions": ["potx"]
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml-template": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["xlsx"]
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template": {
+ "source": "apache",
+ "extensions": ["xltx"]
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.theme+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.themeoverride+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.vmldrawing": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml-template": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["docx"]
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template": {
+ "source": "apache",
+ "extensions": ["dotx"]
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-package.core-properties+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml": {
+ "source": "iana"
+ },
+ "application/vnd.openxmlformats-package.relationships+xml": {
+ "source": "iana"
+ },
+ "application/vnd.oracle.resource+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.orange.indata": {
+ "source": "iana"
+ },
+ "application/vnd.osa.netdeploy": {
+ "source": "iana"
+ },
+ "application/vnd.osgeo.mapguide.package": {
+ "source": "iana",
+ "extensions": ["mgp"]
+ },
+ "application/vnd.osgi.bundle": {
+ "source": "iana"
+ },
+ "application/vnd.osgi.dp": {
+ "source": "iana",
+ "extensions": ["dp"]
+ },
+ "application/vnd.osgi.subsystem": {
+ "source": "iana",
+ "extensions": ["esa"]
+ },
+ "application/vnd.otps.ct-kip+xml": {
+ "source": "iana"
+ },
+ "application/vnd.palm": {
+ "source": "iana",
+ "extensions": ["pdb","pqa","oprc"]
+ },
+ "application/vnd.panoply": {
+ "source": "iana"
+ },
+ "application/vnd.paos+xml": {
+ "source": "iana"
+ },
+ "application/vnd.paos.xml": {
+ "source": "apache"
+ },
+ "application/vnd.pawaafile": {
+ "source": "iana",
+ "extensions": ["paw"]
+ },
+ "application/vnd.pcos": {
+ "source": "iana"
+ },
+ "application/vnd.pg.format": {
+ "source": "iana",
+ "extensions": ["str"]
+ },
+ "application/vnd.pg.osasli": {
+ "source": "iana",
+ "extensions": ["ei6"]
+ },
+ "application/vnd.piaccess.application-licence": {
+ "source": "iana"
+ },
+ "application/vnd.picsel": {
+ "source": "iana",
+ "extensions": ["efif"]
+ },
+ "application/vnd.pmi.widget": {
+ "source": "iana",
+ "extensions": ["wg"]
+ },
+ "application/vnd.poc.group-advertisement+xml": {
+ "source": "iana"
+ },
+ "application/vnd.pocketlearn": {
+ "source": "iana",
+ "extensions": ["plf"]
+ },
+ "application/vnd.powerbuilder6": {
+ "source": "iana",
+ "extensions": ["pbd"]
+ },
+ "application/vnd.powerbuilder6-s": {
+ "source": "iana"
+ },
+ "application/vnd.powerbuilder7": {
+ "source": "iana"
+ },
+ "application/vnd.powerbuilder7-s": {
+ "source": "iana"
+ },
+ "application/vnd.powerbuilder75": {
+ "source": "iana"
+ },
+ "application/vnd.powerbuilder75-s": {
+ "source": "iana"
+ },
+ "application/vnd.preminet": {
+ "source": "iana"
+ },
+ "application/vnd.previewsystems.box": {
+ "source": "iana",
+ "extensions": ["box"]
+ },
+ "application/vnd.proteus.magazine": {
+ "source": "iana",
+ "extensions": ["mgz"]
+ },
+ "application/vnd.publishare-delta-tree": {
+ "source": "iana",
+ "extensions": ["qps"]
+ },
+ "application/vnd.pvi.ptid1": {
+ "source": "iana",
+ "extensions": ["ptid"]
+ },
+ "application/vnd.pwg-multiplexed": {
+ "source": "iana"
+ },
+ "application/vnd.pwg-xhtml-print+xml": {
+ "source": "iana"
+ },
+ "application/vnd.qualcomm.brew-app-res": {
+ "source": "iana"
+ },
+ "application/vnd.quark.quarkxpress": {
+ "source": "iana",
+ "extensions": ["qxd","qxt","qwd","qwt","qxl","qxb"]
+ },
+ "application/vnd.quobject-quoxdocument": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.moml+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-audit+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-audit-conf+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-audit-conn+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-audit-dialog+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-audit-stream+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-conf+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog-base+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog-fax-detect+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog-fax-sendrecv+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog-group+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog-speech+xml": {
+ "source": "iana"
+ },
+ "application/vnd.radisys.msml-dialog-transform+xml": {
+ "source": "iana"
+ },
+ "application/vnd.rainstor.data": {
+ "source": "iana"
+ },
+ "application/vnd.rapid": {
+ "source": "iana"
+ },
+ "application/vnd.realvnc.bed": {
+ "source": "iana",
+ "extensions": ["bed"]
+ },
+ "application/vnd.recordare.musicxml": {
+ "source": "iana",
+ "extensions": ["mxl"]
+ },
+ "application/vnd.recordare.musicxml+xml": {
+ "source": "iana",
+ "extensions": ["musicxml"]
+ },
+ "application/vnd.renlearn.rlprint": {
+ "source": "iana"
+ },
+ "application/vnd.rig.cryptonote": {
+ "source": "iana",
+ "extensions": ["cryptonote"]
+ },
+ "application/vnd.rim.cod": {
+ "source": "apache",
+ "extensions": ["cod"]
+ },
+ "application/vnd.rn-realmedia": {
+ "source": "apache",
+ "extensions": ["rm"]
+ },
+ "application/vnd.rn-realmedia-vbr": {
+ "source": "apache",
+ "extensions": ["rmvb"]
+ },
+ "application/vnd.route66.link66+xml": {
+ "source": "iana",
+ "extensions": ["link66"]
+ },
+ "application/vnd.rs-274x": {
+ "source": "iana"
+ },
+ "application/vnd.ruckus.download": {
+ "source": "iana"
+ },
+ "application/vnd.s3sms": {
+ "source": "iana"
+ },
+ "application/vnd.sailingtracker.track": {
+ "source": "iana",
+ "extensions": ["st"]
+ },
+ "application/vnd.sbm.cid": {
+ "source": "iana"
+ },
+ "application/vnd.sbm.mid2": {
+ "source": "iana"
+ },
+ "application/vnd.scribus": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.3df": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.csf": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.doc": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.eml": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.mht": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.net": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.ppt": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.tiff": {
+ "source": "iana"
+ },
+ "application/vnd.sealed.xls": {
+ "source": "iana"
+ },
+ "application/vnd.sealedmedia.softseal.html": {
+ "source": "iana"
+ },
+ "application/vnd.sealedmedia.softseal.pdf": {
+ "source": "iana"
+ },
+ "application/vnd.seemail": {
+ "source": "iana",
+ "extensions": ["see"]
+ },
+ "application/vnd.sema": {
+ "source": "iana",
+ "extensions": ["sema"]
+ },
+ "application/vnd.semd": {
+ "source": "iana",
+ "extensions": ["semd"]
+ },
+ "application/vnd.semf": {
+ "source": "iana",
+ "extensions": ["semf"]
+ },
+ "application/vnd.shana.informed.formdata": {
+ "source": "iana",
+ "extensions": ["ifm"]
+ },
+ "application/vnd.shana.informed.formtemplate": {
+ "source": "iana",
+ "extensions": ["itp"]
+ },
+ "application/vnd.shana.informed.interchange": {
+ "source": "iana",
+ "extensions": ["iif"]
+ },
+ "application/vnd.shana.informed.package": {
+ "source": "iana",
+ "extensions": ["ipk"]
+ },
+ "application/vnd.simtech-mindmapper": {
+ "source": "iana",
+ "extensions": ["twd","twds"]
+ },
+ "application/vnd.siren+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.smaf": {
+ "source": "iana",
+ "extensions": ["mmf"]
+ },
+ "application/vnd.smart.notebook": {
+ "source": "iana"
+ },
+ "application/vnd.smart.teacher": {
+ "source": "iana",
+ "extensions": ["teacher"]
+ },
+ "application/vnd.software602.filler.form+xml": {
+ "source": "iana"
+ },
+ "application/vnd.software602.filler.form-xml-zip": {
+ "source": "iana"
+ },
+ "application/vnd.solent.sdkm+xml": {
+ "source": "iana",
+ "extensions": ["sdkm","sdkd"]
+ },
+ "application/vnd.spotfire.dxp": {
+ "source": "iana",
+ "extensions": ["dxp"]
+ },
+ "application/vnd.spotfire.sfs": {
+ "source": "iana",
+ "extensions": ["sfs"]
+ },
+ "application/vnd.sss-cod": {
+ "source": "iana"
+ },
+ "application/vnd.sss-dtf": {
+ "source": "iana"
+ },
+ "application/vnd.sss-ntf": {
+ "source": "iana"
+ },
+ "application/vnd.stardivision.calc": {
+ "source": "apache",
+ "extensions": ["sdc"]
+ },
+ "application/vnd.stardivision.draw": {
+ "source": "apache",
+ "extensions": ["sda"]
+ },
+ "application/vnd.stardivision.impress": {
+ "source": "apache",
+ "extensions": ["sdd"]
+ },
+ "application/vnd.stardivision.math": {
+ "source": "apache",
+ "extensions": ["smf"]
+ },
+ "application/vnd.stardivision.writer": {
+ "source": "apache",
+ "extensions": ["sdw","vor"]
+ },
+ "application/vnd.stardivision.writer-global": {
+ "source": "apache",
+ "extensions": ["sgl"]
+ },
+ "application/vnd.stepmania.package": {
+ "source": "iana",
+ "extensions": ["smzip"]
+ },
+ "application/vnd.stepmania.stepchart": {
+ "source": "iana",
+ "extensions": ["sm"]
+ },
+ "application/vnd.street-stream": {
+ "source": "iana"
+ },
+ "application/vnd.sun.wadl+xml": {
+ "source": "iana"
+ },
+ "application/vnd.sun.xml.calc": {
+ "source": "apache",
+ "extensions": ["sxc"]
+ },
+ "application/vnd.sun.xml.calc.template": {
+ "source": "apache",
+ "extensions": ["stc"]
+ },
+ "application/vnd.sun.xml.draw": {
+ "source": "apache",
+ "extensions": ["sxd"]
+ },
+ "application/vnd.sun.xml.draw.template": {
+ "source": "apache",
+ "extensions": ["std"]
+ },
+ "application/vnd.sun.xml.impress": {
+ "source": "apache",
+ "extensions": ["sxi"]
+ },
+ "application/vnd.sun.xml.impress.template": {
+ "source": "apache",
+ "extensions": ["sti"]
+ },
+ "application/vnd.sun.xml.math": {
+ "source": "apache",
+ "extensions": ["sxm"]
+ },
+ "application/vnd.sun.xml.writer": {
+ "source": "apache",
+ "extensions": ["sxw"]
+ },
+ "application/vnd.sun.xml.writer.global": {
+ "source": "apache",
+ "extensions": ["sxg"]
+ },
+ "application/vnd.sun.xml.writer.template": {
+ "source": "apache",
+ "extensions": ["stw"]
+ },
+ "application/vnd.sus-calendar": {
+ "source": "iana",
+ "extensions": ["sus","susp"]
+ },
+ "application/vnd.svd": {
+ "source": "iana",
+ "extensions": ["svd"]
+ },
+ "application/vnd.swiftview-ics": {
+ "source": "iana"
+ },
+ "application/vnd.symbian.install": {
+ "source": "apache",
+ "extensions": ["sis","sisx"]
+ },
+ "application/vnd.syncml+xml": {
+ "source": "iana",
+ "extensions": ["xsm"]
+ },
+ "application/vnd.syncml.dm+wbxml": {
+ "source": "iana",
+ "extensions": ["bdm"]
+ },
+ "application/vnd.syncml.dm+xml": {
+ "source": "iana",
+ "extensions": ["xdm"]
+ },
+ "application/vnd.syncml.dm.notification": {
+ "source": "iana"
+ },
+ "application/vnd.syncml.dmddf+wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.syncml.dmddf+xml": {
+ "source": "iana"
+ },
+ "application/vnd.syncml.dmtnds+wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.syncml.dmtnds+xml": {
+ "source": "iana"
+ },
+ "application/vnd.syncml.ds.notification": {
+ "source": "iana"
+ },
+ "application/vnd.tao.intent-module-archive": {
+ "source": "iana",
+ "extensions": ["tao"]
+ },
+ "application/vnd.tcpdump.pcap": {
+ "source": "iana",
+ "extensions": ["pcap","cap","dmp"]
+ },
+ "application/vnd.tmd.mediaflex.api+xml": {
+ "source": "iana"
+ },
+ "application/vnd.tmobile-livetv": {
+ "source": "iana",
+ "extensions": ["tmo"]
+ },
+ "application/vnd.trid.tpt": {
+ "source": "iana",
+ "extensions": ["tpt"]
+ },
+ "application/vnd.triscape.mxs": {
+ "source": "iana",
+ "extensions": ["mxs"]
+ },
+ "application/vnd.trueapp": {
+ "source": "iana",
+ "extensions": ["tra"]
+ },
+ "application/vnd.truedoc": {
+ "source": "iana"
+ },
+ "application/vnd.ubisoft.webplayer": {
+ "source": "iana"
+ },
+ "application/vnd.ufdl": {
+ "source": "iana",
+ "extensions": ["ufd","ufdl"]
+ },
+ "application/vnd.uiq.theme": {
+ "source": "iana",
+ "extensions": ["utz"]
+ },
+ "application/vnd.umajin": {
+ "source": "iana",
+ "extensions": ["umj"]
+ },
+ "application/vnd.unity": {
+ "source": "iana",
+ "extensions": ["unityweb"]
+ },
+ "application/vnd.uoml+xml": {
+ "source": "iana",
+ "extensions": ["uoml"]
+ },
+ "application/vnd.uplanet.alert": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.alert-wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.bearer-choice": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.bearer-choice-wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.cacheop": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.cacheop-wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.channel": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.channel-wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.list": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.list-wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.listcmd": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.listcmd-wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.uplanet.signal": {
+ "source": "iana"
+ },
+ "application/vnd.valve.source.material": {
+ "source": "iana"
+ },
+ "application/vnd.vcx": {
+ "source": "iana",
+ "extensions": ["vcx"]
+ },
+ "application/vnd.vd-study": {
+ "source": "iana"
+ },
+ "application/vnd.vectorworks": {
+ "source": "iana"
+ },
+ "application/vnd.verimatrix.vcas": {
+ "source": "iana"
+ },
+ "application/vnd.vidsoft.vidconference": {
+ "source": "iana"
+ },
+ "application/vnd.visio": {
+ "source": "iana",
+ "extensions": ["vsd","vst","vss","vsw"]
+ },
+ "application/vnd.visionary": {
+ "source": "iana",
+ "extensions": ["vis"]
+ },
+ "application/vnd.vividence.scriptfile": {
+ "source": "iana"
+ },
+ "application/vnd.vsf": {
+ "source": "iana",
+ "extensions": ["vsf"]
+ },
+ "application/vnd.wap.sic": {
+ "source": "iana"
+ },
+ "application/vnd.wap.slc": {
+ "source": "iana"
+ },
+ "application/vnd.wap.wbxml": {
+ "source": "iana",
+ "extensions": ["wbxml"]
+ },
+ "application/vnd.wap.wmlc": {
+ "source": "iana",
+ "extensions": ["wmlc"]
+ },
+ "application/vnd.wap.wmlscriptc": {
+ "source": "iana",
+ "extensions": ["wmlsc"]
+ },
+ "application/vnd.webturbo": {
+ "source": "iana",
+ "extensions": ["wtb"]
+ },
+ "application/vnd.wfa.p2p": {
+ "source": "iana"
+ },
+ "application/vnd.wfa.wsc": {
+ "source": "iana"
+ },
+ "application/vnd.windows.devicepairing": {
+ "source": "iana"
+ },
+ "application/vnd.wmc": {
+ "source": "iana"
+ },
+ "application/vnd.wmf.bootstrap": {
+ "source": "iana"
+ },
+ "application/vnd.wolfram.mathematica": {
+ "source": "iana"
+ },
+ "application/vnd.wolfram.mathematica.package": {
+ "source": "iana"
+ },
+ "application/vnd.wolfram.player": {
+ "source": "iana",
+ "extensions": ["nbp"]
+ },
+ "application/vnd.wordperfect": {
+ "source": "iana",
+ "extensions": ["wpd"]
+ },
+ "application/vnd.wqd": {
+ "source": "iana",
+ "extensions": ["wqd"]
+ },
+ "application/vnd.wrq-hp3000-labelled": {
+ "source": "iana"
+ },
+ "application/vnd.wt.stf": {
+ "source": "iana",
+ "extensions": ["stf"]
+ },
+ "application/vnd.wv.csp+wbxml": {
+ "source": "iana"
+ },
+ "application/vnd.wv.csp+xml": {
+ "source": "iana"
+ },
+ "application/vnd.wv.ssp+xml": {
+ "source": "iana"
+ },
+ "application/vnd.xacml+json": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/vnd.xara": {
+ "source": "iana",
+ "extensions": ["xar"]
+ },
+ "application/vnd.xfdl": {
+ "source": "iana",
+ "extensions": ["xfdl"]
+ },
+ "application/vnd.xfdl.webform": {
+ "source": "iana"
+ },
+ "application/vnd.xmi+xml": {
+ "source": "iana"
+ },
+ "application/vnd.xmpie.cpkg": {
+ "source": "iana"
+ },
+ "application/vnd.xmpie.dpkg": {
+ "source": "iana"
+ },
+ "application/vnd.xmpie.plan": {
+ "source": "iana"
+ },
+ "application/vnd.xmpie.ppkg": {
+ "source": "iana"
+ },
+ "application/vnd.xmpie.xlim": {
+ "source": "iana"
+ },
+ "application/vnd.yamaha.hv-dic": {
+ "source": "iana",
+ "extensions": ["hvd"]
+ },
+ "application/vnd.yamaha.hv-script": {
+ "source": "iana",
+ "extensions": ["hvs"]
+ },
+ "application/vnd.yamaha.hv-voice": {
+ "source": "iana",
+ "extensions": ["hvp"]
+ },
+ "application/vnd.yamaha.openscoreformat": {
+ "source": "iana",
+ "extensions": ["osf"]
+ },
+ "application/vnd.yamaha.openscoreformat.osfpvg+xml": {
+ "source": "iana",
+ "extensions": ["osfpvg"]
+ },
+ "application/vnd.yamaha.remote-setup": {
+ "source": "iana"
+ },
+ "application/vnd.yamaha.smaf-audio": {
+ "source": "iana",
+ "extensions": ["saf"]
+ },
+ "application/vnd.yamaha.smaf-phrase": {
+ "source": "iana",
+ "extensions": ["spf"]
+ },
+ "application/vnd.yamaha.through-ngn": {
+ "source": "iana"
+ },
+ "application/vnd.yamaha.tunnel-udpencap": {
+ "source": "iana"
+ },
+ "application/vnd.yaoweme": {
+ "source": "iana"
+ },
+ "application/vnd.yellowriver-custom-menu": {
+ "source": "iana",
+ "extensions": ["cmp"]
+ },
+ "application/vnd.zul": {
+ "source": "iana",
+ "extensions": ["zir","zirz"]
+ },
+ "application/vnd.zzazz.deck+xml": {
+ "source": "iana",
+ "extensions": ["zaz"]
+ },
+ "application/voicexml+xml": {
+ "source": "iana",
+ "extensions": ["vxml"]
+ },
+ "application/vq-rtcpxr": {
+ "source": "iana"
+ },
+ "application/watcherinfo+xml": {
+ "source": "iana"
+ },
+ "application/whoispp-query": {
+ "source": "iana"
+ },
+ "application/whoispp-response": {
+ "source": "iana"
+ },
+ "application/widget": {
+ "source": "iana",
+ "extensions": ["wgt"]
+ },
+ "application/winhlp": {
+ "source": "apache",
+ "extensions": ["hlp"]
+ },
+ "application/wita": {
+ "source": "iana"
+ },
+ "application/wordperfect5.1": {
+ "source": "iana"
+ },
+ "application/wsdl+xml": {
+ "source": "iana",
+ "extensions": ["wsdl"]
+ },
+ "application/wspolicy+xml": {
+ "source": "iana",
+ "extensions": ["wspolicy"]
+ },
+ "application/x-7z-compressed": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["7z"]
+ },
+ "application/x-abiword": {
+ "source": "apache",
+ "extensions": ["abw"]
+ },
+ "application/x-ace-compressed": {
+ "source": "apache",
+ "extensions": ["ace"]
+ },
+ "application/x-amf": {
+ "source": "apache"
+ },
+ "application/x-apple-diskimage": {
+ "source": "apache",
+ "extensions": ["dmg"]
+ },
+ "application/x-authorware-bin": {
+ "source": "apache",
+ "extensions": ["aab","x32","u32","vox"]
+ },
+ "application/x-authorware-map": {
+ "source": "apache",
+ "extensions": ["aam"]
+ },
+ "application/x-authorware-seg": {
+ "source": "apache",
+ "extensions": ["aas"]
+ },
+ "application/x-bcpio": {
+ "source": "apache",
+ "extensions": ["bcpio"]
+ },
+ "application/x-bittorrent": {
+ "source": "apache",
+ "extensions": ["torrent"]
+ },
+ "application/x-blorb": {
+ "source": "apache",
+ "extensions": ["blb","blorb"]
+ },
+ "application/x-bzip": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["bz"]
+ },
+ "application/x-bzip2": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["bz2","boz"]
+ },
+ "application/x-cbr": {
+ "source": "apache",
+ "extensions": ["cbr","cba","cbt","cbz","cb7"]
+ },
+ "application/x-cdlink": {
+ "source": "apache",
+ "extensions": ["vcd"]
+ },
+ "application/x-cfs-compressed": {
+ "source": "apache",
+ "extensions": ["cfs"]
+ },
+ "application/x-chat": {
+ "source": "apache",
+ "extensions": ["chat"]
+ },
+ "application/x-chess-pgn": {
+ "source": "apache",
+ "extensions": ["pgn"]
+ },
+ "application/x-chrome-extension": {
+ "extensions": ["crx"]
+ },
+ "application/x-compress": {
+ "source": "apache"
+ },
+ "application/x-conference": {
+ "source": "apache",
+ "extensions": ["nsc"]
+ },
+ "application/x-cpio": {
+ "source": "apache",
+ "extensions": ["cpio"]
+ },
+ "application/x-csh": {
+ "source": "apache",
+ "extensions": ["csh"]
+ },
+ "application/x-deb": {
+ "compressible": false
+ },
+ "application/x-debian-package": {
+ "source": "apache",
+ "extensions": ["deb","udeb"]
+ },
+ "application/x-dgc-compressed": {
+ "source": "apache",
+ "extensions": ["dgc"]
+ },
+ "application/x-director": {
+ "source": "apache",
+ "extensions": ["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"]
+ },
+ "application/x-doom": {
+ "source": "apache",
+ "extensions": ["wad"]
+ },
+ "application/x-dtbncx+xml": {
+ "source": "apache",
+ "extensions": ["ncx"]
+ },
+ "application/x-dtbook+xml": {
+ "source": "apache",
+ "extensions": ["dtb"]
+ },
+ "application/x-dtbresource+xml": {
+ "source": "apache",
+ "extensions": ["res"]
+ },
+ "application/x-dvi": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["dvi"]
+ },
+ "application/x-envoy": {
+ "source": "apache",
+ "extensions": ["evy"]
+ },
+ "application/x-eva": {
+ "source": "apache",
+ "extensions": ["eva"]
+ },
+ "application/x-font-bdf": {
+ "source": "apache",
+ "extensions": ["bdf"]
+ },
+ "application/x-font-dos": {
+ "source": "apache"
+ },
+ "application/x-font-framemaker": {
+ "source": "apache"
+ },
+ "application/x-font-ghostscript": {
+ "source": "apache",
+ "extensions": ["gsf"]
+ },
+ "application/x-font-libgrx": {
+ "source": "apache"
+ },
+ "application/x-font-linux-psf": {
+ "source": "apache",
+ "extensions": ["psf"]
+ },
+ "application/x-font-otf": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["otf"]
+ },
+ "application/x-font-pcf": {
+ "source": "apache",
+ "extensions": ["pcf"]
+ },
+ "application/x-font-snf": {
+ "source": "apache",
+ "extensions": ["snf"]
+ },
+ "application/x-font-speedo": {
+ "source": "apache"
+ },
+ "application/x-font-sunos-news": {
+ "source": "apache"
+ },
+ "application/x-font-ttf": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["ttf","ttc"]
+ },
+ "application/x-font-type1": {
+ "source": "apache",
+ "extensions": ["pfa","pfb","pfm","afm"]
+ },
+ "application/x-font-vfont": {
+ "source": "apache"
+ },
+ "application/x-freearc": {
+ "source": "apache",
+ "extensions": ["arc"]
+ },
+ "application/x-futuresplash": {
+ "source": "apache",
+ "extensions": ["spl"]
+ },
+ "application/x-gca-compressed": {
+ "source": "apache",
+ "extensions": ["gca"]
+ },
+ "application/x-glulx": {
+ "source": "apache",
+ "extensions": ["ulx"]
+ },
+ "application/x-gnumeric": {
+ "source": "apache",
+ "extensions": ["gnumeric"]
+ },
+ "application/x-gramps-xml": {
+ "source": "apache",
+ "extensions": ["gramps"]
+ },
+ "application/x-gtar": {
+ "source": "apache",
+ "extensions": ["gtar"]
+ },
+ "application/x-gzip": {
+ "source": "apache"
+ },
+ "application/x-hdf": {
+ "source": "apache",
+ "extensions": ["hdf"]
+ },
+ "application/x-install-instructions": {
+ "source": "apache",
+ "extensions": ["install"]
+ },
+ "application/x-iso9660-image": {
+ "source": "apache",
+ "extensions": ["iso"]
+ },
+ "application/x-java-jnlp-file": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["jnlp"]
+ },
+ "application/x-javascript": {
+ "compressible": true
+ },
+ "application/x-latex": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["latex"]
+ },
+ "application/x-lua-bytecode": {
+ "extensions": ["luac"]
+ },
+ "application/x-lzh-compressed": {
+ "source": "apache",
+ "extensions": ["lzh","lha"]
+ },
+ "application/x-mie": {
+ "source": "apache",
+ "extensions": ["mie"]
+ },
+ "application/x-mobipocket-ebook": {
+ "source": "apache",
+ "extensions": ["prc","mobi"]
+ },
+ "application/x-mpegurl": {
+ "compressible": false
+ },
+ "application/x-ms-application": {
+ "source": "apache",
+ "extensions": ["application"]
+ },
+ "application/x-ms-shortcut": {
+ "source": "apache",
+ "extensions": ["lnk"]
+ },
+ "application/x-ms-wmd": {
+ "source": "apache",
+ "extensions": ["wmd"]
+ },
+ "application/x-ms-wmz": {
+ "source": "apache",
+ "extensions": ["wmz"]
+ },
+ "application/x-ms-xbap": {
+ "source": "apache",
+ "extensions": ["xbap"]
+ },
+ "application/x-msaccess": {
+ "source": "apache",
+ "extensions": ["mdb"]
+ },
+ "application/x-msbinder": {
+ "source": "apache",
+ "extensions": ["obd"]
+ },
+ "application/x-mscardfile": {
+ "source": "apache",
+ "extensions": ["crd"]
+ },
+ "application/x-msclip": {
+ "source": "apache",
+ "extensions": ["clp"]
+ },
+ "application/x-msdownload": {
+ "source": "apache",
+ "extensions": ["exe","dll","com","bat","msi"]
+ },
+ "application/x-msmediaview": {
+ "source": "apache",
+ "extensions": ["mvb","m13","m14"]
+ },
+ "application/x-msmetafile": {
+ "source": "apache",
+ "extensions": ["wmf","wmz","emf","emz"]
+ },
+ "application/x-msmoney": {
+ "source": "apache",
+ "extensions": ["mny"]
+ },
+ "application/x-mspublisher": {
+ "source": "apache",
+ "extensions": ["pub"]
+ },
+ "application/x-msschedule": {
+ "source": "apache",
+ "extensions": ["scd"]
+ },
+ "application/x-msterminal": {
+ "source": "apache",
+ "extensions": ["trm"]
+ },
+ "application/x-mswrite": {
+ "source": "apache",
+ "extensions": ["wri"]
+ },
+ "application/x-netcdf": {
+ "source": "apache",
+ "extensions": ["nc","cdf"]
+ },
+ "application/x-ns-proxy-autoconfig": {
+ "compressible": true,
+ "extensions": ["pac"]
+ },
+ "application/x-nzb": {
+ "source": "apache",
+ "extensions": ["nzb"]
+ },
+ "application/x-pkcs12": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["p12","pfx"]
+ },
+ "application/x-pkcs7-certificates": {
+ "source": "apache",
+ "extensions": ["p7b","spc"]
+ },
+ "application/x-pkcs7-certreqresp": {
+ "source": "apache",
+ "extensions": ["p7r"]
+ },
+ "application/x-rar-compressed": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["rar"]
+ },
+ "application/x-research-info-systems": {
+ "source": "apache",
+ "extensions": ["ris"]
+ },
+ "application/x-sh": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["sh"]
+ },
+ "application/x-shar": {
+ "source": "apache",
+ "extensions": ["shar"]
+ },
+ "application/x-shockwave-flash": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["swf"]
+ },
+ "application/x-silverlight-app": {
+ "source": "apache",
+ "extensions": ["xap"]
+ },
+ "application/x-sql": {
+ "source": "apache",
+ "extensions": ["sql"]
+ },
+ "application/x-stuffit": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["sit"]
+ },
+ "application/x-stuffitx": {
+ "source": "apache",
+ "extensions": ["sitx"]
+ },
+ "application/x-subrip": {
+ "source": "apache",
+ "extensions": ["srt"]
+ },
+ "application/x-sv4cpio": {
+ "source": "apache",
+ "extensions": ["sv4cpio"]
+ },
+ "application/x-sv4crc": {
+ "source": "apache",
+ "extensions": ["sv4crc"]
+ },
+ "application/x-t3vm-image": {
+ "source": "apache",
+ "extensions": ["t3"]
+ },
+ "application/x-tads": {
+ "source": "apache",
+ "extensions": ["gam"]
+ },
+ "application/x-tar": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["tar"]
+ },
+ "application/x-tcl": {
+ "source": "apache",
+ "extensions": ["tcl"]
+ },
+ "application/x-tex": {
+ "source": "apache",
+ "extensions": ["tex"]
+ },
+ "application/x-tex-tfm": {
+ "source": "apache",
+ "extensions": ["tfm"]
+ },
+ "application/x-texinfo": {
+ "source": "apache",
+ "extensions": ["texinfo","texi"]
+ },
+ "application/x-tgif": {
+ "source": "apache",
+ "extensions": ["obj"]
+ },
+ "application/x-ustar": {
+ "source": "apache",
+ "extensions": ["ustar"]
+ },
+ "application/x-wais-source": {
+ "source": "apache",
+ "extensions": ["src"]
+ },
+ "application/x-web-app-manifest+json": {
+ "compressible": true,
+ "extensions": ["webapp"]
+ },
+ "application/x-www-form-urlencoded": {
+ "source": "iana",
+ "compressible": true
+ },
+ "application/x-x509-ca-cert": {
+ "source": "apache",
+ "extensions": ["der","crt"]
+ },
+ "application/x-xfig": {
+ "source": "apache",
+ "extensions": ["fig"]
+ },
+ "application/x-xliff+xml": {
+ "source": "apache",
+ "extensions": ["xlf"]
+ },
+ "application/x-xpinstall": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["xpi"]
+ },
+ "application/x-xz": {
+ "source": "apache",
+ "extensions": ["xz"]
+ },
+ "application/x-zmachine": {
+ "source": "apache",
+ "extensions": ["z1","z2","z3","z4","z5","z6","z7","z8"]
+ },
+ "application/x400-bp": {
+ "source": "iana"
+ },
+ "application/xacml+xml": {
+ "source": "iana"
+ },
+ "application/xaml+xml": {
+ "source": "apache",
+ "extensions": ["xaml"]
+ },
+ "application/xcap-att+xml": {
+ "source": "iana"
+ },
+ "application/xcap-caps+xml": {
+ "source": "iana"
+ },
+ "application/xcap-diff+xml": {
+ "source": "iana",
+ "extensions": ["xdf"]
+ },
+ "application/xcap-el+xml": {
+ "source": "iana"
+ },
+ "application/xcap-error+xml": {
+ "source": "iana"
+ },
+ "application/xcap-ns+xml": {
+ "source": "iana"
+ },
+ "application/xcon-conference-info+xml": {
+ "source": "iana"
+ },
+ "application/xcon-conference-info-diff+xml": {
+ "source": "iana"
+ },
+ "application/xenc+xml": {
+ "source": "iana",
+ "extensions": ["xenc"]
+ },
+ "application/xhtml+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["xhtml","xht"]
+ },
+ "application/xhtml-voice+xml": {
+ "source": "apache"
+ },
+ "application/xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["xml","xsl","xsd"]
+ },
+ "application/xml-dtd": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["dtd"]
+ },
+ "application/xml-external-parsed-entity": {
+ "source": "iana"
+ },
+ "application/xml-patch+xml": {
+ "source": "iana"
+ },
+ "application/xmpp+xml": {
+ "source": "iana"
+ },
+ "application/xop+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["xop"]
+ },
+ "application/xproc+xml": {
+ "source": "apache",
+ "extensions": ["xpl"]
+ },
+ "application/xslt+xml": {
+ "source": "iana",
+ "extensions": ["xslt"]
+ },
+ "application/xspf+xml": {
+ "source": "apache",
+ "extensions": ["xspf"]
+ },
+ "application/xv+xml": {
+ "source": "iana",
+ "extensions": ["mxml","xhvml","xvml","xvm"]
+ },
+ "application/yang": {
+ "source": "iana",
+ "extensions": ["yang"]
+ },
+ "application/yin+xml": {
+ "source": "iana",
+ "extensions": ["yin"]
+ },
+ "application/zip": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["zip"]
+ },
+ "application/zlib": {
+ "source": "iana"
+ },
+ "audio/1d-interleaved-parityfec": {
+ "source": "iana"
+ },
+ "audio/32kadpcm": {
+ "source": "iana"
+ },
+ "audio/3gpp": {
+ "source": "iana"
+ },
+ "audio/3gpp2": {
+ "source": "iana"
+ },
+ "audio/ac3": {
+ "source": "iana"
+ },
+ "audio/adpcm": {
+ "source": "apache",
+ "extensions": ["adp"]
+ },
+ "audio/amr": {
+ "source": "iana"
+ },
+ "audio/amr-wb": {
+ "source": "iana"
+ },
+ "audio/amr-wb+": {
+ "source": "iana"
+ },
+ "audio/aptx": {
+ "source": "iana"
+ },
+ "audio/asc": {
+ "source": "iana"
+ },
+ "audio/atrac-advanced-lossless": {
+ "source": "iana"
+ },
+ "audio/atrac-x": {
+ "source": "iana"
+ },
+ "audio/atrac3": {
+ "source": "iana"
+ },
+ "audio/basic": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["au","snd"]
+ },
+ "audio/bv16": {
+ "source": "iana"
+ },
+ "audio/bv32": {
+ "source": "iana"
+ },
+ "audio/clearmode": {
+ "source": "iana"
+ },
+ "audio/cn": {
+ "source": "iana"
+ },
+ "audio/dat12": {
+ "source": "iana"
+ },
+ "audio/dls": {
+ "source": "iana"
+ },
+ "audio/dsr-es201108": {
+ "source": "iana"
+ },
+ "audio/dsr-es202050": {
+ "source": "iana"
+ },
+ "audio/dsr-es202211": {
+ "source": "iana"
+ },
+ "audio/dsr-es202212": {
+ "source": "iana"
+ },
+ "audio/dv": {
+ "source": "iana"
+ },
+ "audio/dvi4": {
+ "source": "iana"
+ },
+ "audio/eac3": {
+ "source": "iana"
+ },
+ "audio/encaprtp": {
+ "source": "iana"
+ },
+ "audio/evrc": {
+ "source": "iana"
+ },
+ "audio/evrc-qcp": {
+ "source": "iana"
+ },
+ "audio/evrc0": {
+ "source": "iana"
+ },
+ "audio/evrc1": {
+ "source": "iana"
+ },
+ "audio/evrcb": {
+ "source": "iana"
+ },
+ "audio/evrcb0": {
+ "source": "iana"
+ },
+ "audio/evrcb1": {
+ "source": "iana"
+ },
+ "audio/evrcnw": {
+ "source": "iana"
+ },
+ "audio/evrcnw0": {
+ "source": "iana"
+ },
+ "audio/evrcnw1": {
+ "source": "iana"
+ },
+ "audio/evrcwb": {
+ "source": "iana"
+ },
+ "audio/evrcwb0": {
+ "source": "iana"
+ },
+ "audio/evrcwb1": {
+ "source": "iana"
+ },
+ "audio/fwdred": {
+ "source": "iana"
+ },
+ "audio/g719": {
+ "source": "iana"
+ },
+ "audio/g722": {
+ "source": "iana"
+ },
+ "audio/g7221": {
+ "source": "iana"
+ },
+ "audio/g723": {
+ "source": "iana"
+ },
+ "audio/g726-16": {
+ "source": "iana"
+ },
+ "audio/g726-24": {
+ "source": "iana"
+ },
+ "audio/g726-32": {
+ "source": "iana"
+ },
+ "audio/g726-40": {
+ "source": "iana"
+ },
+ "audio/g728": {
+ "source": "iana"
+ },
+ "audio/g729": {
+ "source": "iana"
+ },
+ "audio/g7291": {
+ "source": "iana"
+ },
+ "audio/g729d": {
+ "source": "iana"
+ },
+ "audio/g729e": {
+ "source": "iana"
+ },
+ "audio/gsm": {
+ "source": "iana"
+ },
+ "audio/gsm-efr": {
+ "source": "iana"
+ },
+ "audio/gsm-hr-08": {
+ "source": "iana"
+ },
+ "audio/ilbc": {
+ "source": "iana"
+ },
+ "audio/ip-mr_v2.5": {
+ "source": "iana"
+ },
+ "audio/isac": {
+ "source": "apache"
+ },
+ "audio/l16": {
+ "source": "iana"
+ },
+ "audio/l20": {
+ "source": "iana"
+ },
+ "audio/l24": {
+ "source": "iana",
+ "compressible": false
+ },
+ "audio/l8": {
+ "source": "iana"
+ },
+ "audio/lpc": {
+ "source": "iana"
+ },
+ "audio/midi": {
+ "source": "apache",
+ "extensions": ["mid","midi","kar","rmi"]
+ },
+ "audio/mobile-xmf": {
+ "source": "iana"
+ },
+ "audio/mp4": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["mp4a","m4a"]
+ },
+ "audio/mp4a-latm": {
+ "source": "iana"
+ },
+ "audio/mpa": {
+ "source": "iana"
+ },
+ "audio/mpa-robust": {
+ "source": "iana"
+ },
+ "audio/mpeg": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["mpga","mp2","mp2a","mp3","m2a","m3a"]
+ },
+ "audio/mpeg4-generic": {
+ "source": "iana"
+ },
+ "audio/musepack": {
+ "source": "apache"
+ },
+ "audio/ogg": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["oga","ogg","spx"]
+ },
+ "audio/opus": {
+ "source": "iana"
+ },
+ "audio/parityfec": {
+ "source": "iana"
+ },
+ "audio/pcma": {
+ "source": "iana"
+ },
+ "audio/pcma-wb": {
+ "source": "iana"
+ },
+ "audio/pcmu": {
+ "source": "iana"
+ },
+ "audio/pcmu-wb": {
+ "source": "iana"
+ },
+ "audio/prs.sid": {
+ "source": "iana"
+ },
+ "audio/qcelp": {
+ "source": "iana"
+ },
+ "audio/raptorfec": {
+ "source": "iana"
+ },
+ "audio/red": {
+ "source": "iana"
+ },
+ "audio/rtp-enc-aescm128": {
+ "source": "iana"
+ },
+ "audio/rtp-midi": {
+ "source": "iana"
+ },
+ "audio/rtploopback": {
+ "source": "iana"
+ },
+ "audio/rtx": {
+ "source": "iana"
+ },
+ "audio/s3m": {
+ "source": "apache",
+ "extensions": ["s3m"]
+ },
+ "audio/silk": {
+ "source": "apache",
+ "extensions": ["sil"]
+ },
+ "audio/smv": {
+ "source": "iana"
+ },
+ "audio/smv-qcp": {
+ "source": "iana"
+ },
+ "audio/smv0": {
+ "source": "iana"
+ },
+ "audio/sp-midi": {
+ "source": "iana"
+ },
+ "audio/speex": {
+ "source": "iana"
+ },
+ "audio/t140c": {
+ "source": "iana"
+ },
+ "audio/t38": {
+ "source": "iana"
+ },
+ "audio/telephone-event": {
+ "source": "iana"
+ },
+ "audio/tone": {
+ "source": "iana"
+ },
+ "audio/uemclip": {
+ "source": "iana"
+ },
+ "audio/ulpfec": {
+ "source": "iana"
+ },
+ "audio/vdvi": {
+ "source": "iana"
+ },
+ "audio/vmr-wb": {
+ "source": "iana"
+ },
+ "audio/vnd.3gpp.iufp": {
+ "source": "iana"
+ },
+ "audio/vnd.4sb": {
+ "source": "iana"
+ },
+ "audio/vnd.audiokoz": {
+ "source": "iana"
+ },
+ "audio/vnd.celp": {
+ "source": "iana"
+ },
+ "audio/vnd.cisco.nse": {
+ "source": "iana"
+ },
+ "audio/vnd.cmles.radio-events": {
+ "source": "iana"
+ },
+ "audio/vnd.cns.anp1": {
+ "source": "iana"
+ },
+ "audio/vnd.cns.inf1": {
+ "source": "iana"
+ },
+ "audio/vnd.dece.audio": {
+ "source": "iana",
+ "extensions": ["uva","uvva"]
+ },
+ "audio/vnd.digital-winds": {
+ "source": "iana",
+ "extensions": ["eol"]
+ },
+ "audio/vnd.dlna.adts": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.heaac.1": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.heaac.2": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.mlp": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.mps": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.pl2": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.pl2x": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.pl2z": {
+ "source": "iana"
+ },
+ "audio/vnd.dolby.pulse.1": {
+ "source": "iana"
+ },
+ "audio/vnd.dra": {
+ "source": "iana",
+ "extensions": ["dra"]
+ },
+ "audio/vnd.dts": {
+ "source": "iana",
+ "extensions": ["dts"]
+ },
+ "audio/vnd.dts.hd": {
+ "source": "iana",
+ "extensions": ["dtshd"]
+ },
+ "audio/vnd.dvb.file": {
+ "source": "iana"
+ },
+ "audio/vnd.everad.plj": {
+ "source": "iana"
+ },
+ "audio/vnd.hns.audio": {
+ "source": "iana"
+ },
+ "audio/vnd.lucent.voice": {
+ "source": "iana",
+ "extensions": ["lvp"]
+ },
+ "audio/vnd.ms-playready.media.pya": {
+ "source": "iana",
+ "extensions": ["pya"]
+ },
+ "audio/vnd.nokia.mobile-xmf": {
+ "source": "iana"
+ },
+ "audio/vnd.nortel.vbk": {
+ "source": "iana"
+ },
+ "audio/vnd.nuera.ecelp4800": {
+ "source": "iana",
+ "extensions": ["ecelp4800"]
+ },
+ "audio/vnd.nuera.ecelp7470": {
+ "source": "iana",
+ "extensions": ["ecelp7470"]
+ },
+ "audio/vnd.nuera.ecelp9600": {
+ "source": "iana",
+ "extensions": ["ecelp9600"]
+ },
+ "audio/vnd.octel.sbc": {
+ "source": "iana"
+ },
+ "audio/vnd.qcelp": {
+ "source": "iana"
+ },
+ "audio/vnd.rhetorex.32kadpcm": {
+ "source": "iana"
+ },
+ "audio/vnd.rip": {
+ "source": "iana",
+ "extensions": ["rip"]
+ },
+ "audio/vnd.rn-realaudio": {
+ "compressible": false
+ },
+ "audio/vnd.sealedmedia.softseal.mpeg": {
+ "source": "iana"
+ },
+ "audio/vnd.vmx.cvsd": {
+ "source": "iana"
+ },
+ "audio/vnd.wave": {
+ "compressible": false
+ },
+ "audio/vorbis": {
+ "source": "iana",
+ "compressible": false
+ },
+ "audio/vorbis-config": {
+ "source": "iana"
+ },
+ "audio/webm": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["weba"]
+ },
+ "audio/x-aac": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["aac"]
+ },
+ "audio/x-aiff": {
+ "source": "apache",
+ "extensions": ["aif","aiff","aifc"]
+ },
+ "audio/x-caf": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["caf"]
+ },
+ "audio/x-flac": {
+ "source": "apache",
+ "extensions": ["flac"]
+ },
+ "audio/x-matroska": {
+ "source": "apache",
+ "extensions": ["mka"]
+ },
+ "audio/x-mpegurl": {
+ "source": "apache",
+ "extensions": ["m3u"]
+ },
+ "audio/x-ms-wax": {
+ "source": "apache",
+ "extensions": ["wax"]
+ },
+ "audio/x-ms-wma": {
+ "source": "apache",
+ "extensions": ["wma"]
+ },
+ "audio/x-pn-realaudio": {
+ "source": "apache",
+ "extensions": ["ram","ra"]
+ },
+ "audio/x-pn-realaudio-plugin": {
+ "source": "apache",
+ "extensions": ["rmp"]
+ },
+ "audio/x-tta": {
+ "source": "apache"
+ },
+ "audio/x-wav": {
+ "source": "apache",
+ "extensions": ["wav"]
+ },
+ "audio/xm": {
+ "source": "apache",
+ "extensions": ["xm"]
+ },
+ "chemical/x-cdx": {
+ "source": "apache",
+ "extensions": ["cdx"]
+ },
+ "chemical/x-cif": {
+ "source": "apache",
+ "extensions": ["cif"]
+ },
+ "chemical/x-cmdf": {
+ "source": "apache",
+ "extensions": ["cmdf"]
+ },
+ "chemical/x-cml": {
+ "source": "apache",
+ "extensions": ["cml"]
+ },
+ "chemical/x-csml": {
+ "source": "apache",
+ "extensions": ["csml"]
+ },
+ "chemical/x-pdb": {
+ "source": "apache"
+ },
+ "chemical/x-xyz": {
+ "source": "apache",
+ "extensions": ["xyz"]
+ },
+ "font/opentype": {
+ "compressible": true,
+ "extensions": ["otf"]
+ },
+ "image/bmp": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["bmp"]
+ },
+ "image/cgm": {
+ "source": "iana",
+ "extensions": ["cgm"]
+ },
+ "image/fits": {
+ "source": "iana"
+ },
+ "image/g3fax": {
+ "source": "iana",
+ "extensions": ["g3"]
+ },
+ "image/gif": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["gif"]
+ },
+ "image/ief": {
+ "source": "iana",
+ "extensions": ["ief"]
+ },
+ "image/jp2": {
+ "source": "iana"
+ },
+ "image/jpeg": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["jpeg","jpg","jpe"]
+ },
+ "image/jpm": {
+ "source": "iana"
+ },
+ "image/jpx": {
+ "source": "iana"
+ },
+ "image/ktx": {
+ "source": "iana",
+ "extensions": ["ktx"]
+ },
+ "image/naplps": {
+ "source": "iana"
+ },
+ "image/pjpeg": {
+ "compressible": false
+ },
+ "image/png": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["png"]
+ },
+ "image/prs.btif": {
+ "source": "iana",
+ "extensions": ["btif"]
+ },
+ "image/prs.pti": {
+ "source": "iana"
+ },
+ "image/pwg-raster": {
+ "source": "iana"
+ },
+ "image/sgi": {
+ "source": "apache",
+ "extensions": ["sgi"]
+ },
+ "image/svg+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["svg","svgz"]
+ },
+ "image/t38": {
+ "source": "iana"
+ },
+ "image/tiff": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["tiff","tif"]
+ },
+ "image/tiff-fx": {
+ "source": "iana"
+ },
+ "image/vnd.adobe.photoshop": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["psd"]
+ },
+ "image/vnd.airzip.accelerator.azv": {
+ "source": "iana"
+ },
+ "image/vnd.cns.inf2": {
+ "source": "iana"
+ },
+ "image/vnd.dece.graphic": {
+ "source": "iana",
+ "extensions": ["uvi","uvvi","uvg","uvvg"]
+ },
+ "image/vnd.djvu": {
+ "source": "iana",
+ "extensions": ["djvu","djv"]
+ },
+ "image/vnd.dvb.subtitle": {
+ "source": "iana",
+ "extensions": ["sub"]
+ },
+ "image/vnd.dwg": {
+ "source": "iana",
+ "extensions": ["dwg"]
+ },
+ "image/vnd.dxf": {
+ "source": "iana",
+ "extensions": ["dxf"]
+ },
+ "image/vnd.fastbidsheet": {
+ "source": "iana",
+ "extensions": ["fbs"]
+ },
+ "image/vnd.fpx": {
+ "source": "iana",
+ "extensions": ["fpx"]
+ },
+ "image/vnd.fst": {
+ "source": "iana",
+ "extensions": ["fst"]
+ },
+ "image/vnd.fujixerox.edmics-mmr": {
+ "source": "iana",
+ "extensions": ["mmr"]
+ },
+ "image/vnd.fujixerox.edmics-rlc": {
+ "source": "iana",
+ "extensions": ["rlc"]
+ },
+ "image/vnd.globalgraphics.pgb": {
+ "source": "iana"
+ },
+ "image/vnd.microsoft.icon": {
+ "source": "iana"
+ },
+ "image/vnd.mix": {
+ "source": "iana"
+ },
+ "image/vnd.ms-modi": {
+ "source": "iana",
+ "extensions": ["mdi"]
+ },
+ "image/vnd.ms-photo": {
+ "source": "apache",
+ "extensions": ["wdp"]
+ },
+ "image/vnd.net-fpx": {
+ "source": "iana",
+ "extensions": ["npx"]
+ },
+ "image/vnd.radiance": {
+ "source": "iana"
+ },
+ "image/vnd.sealed.png": {
+ "source": "iana"
+ },
+ "image/vnd.sealedmedia.softseal.gif": {
+ "source": "iana"
+ },
+ "image/vnd.sealedmedia.softseal.jpg": {
+ "source": "iana"
+ },
+ "image/vnd.svf": {
+ "source": "iana"
+ },
+ "image/vnd.tencent.tap": {
+ "source": "iana"
+ },
+ "image/vnd.valve.source.texture": {
+ "source": "iana"
+ },
+ "image/vnd.wap.wbmp": {
+ "source": "iana",
+ "extensions": ["wbmp"]
+ },
+ "image/vnd.xiff": {
+ "source": "iana",
+ "extensions": ["xif"]
+ },
+ "image/vnd.zbrush.pcx": {
+ "source": "iana"
+ },
+ "image/webp": {
+ "source": "apache",
+ "extensions": ["webp"]
+ },
+ "image/x-3ds": {
+ "source": "apache",
+ "extensions": ["3ds"]
+ },
+ "image/x-cmu-raster": {
+ "source": "apache",
+ "extensions": ["ras"]
+ },
+ "image/x-cmx": {
+ "source": "apache",
+ "extensions": ["cmx"]
+ },
+ "image/x-freehand": {
+ "source": "apache",
+ "extensions": ["fh","fhc","fh4","fh5","fh7"]
+ },
+ "image/x-icon": {
+ "source": "apache",
+ "compressible": true,
+ "extensions": ["ico"]
+ },
+ "image/x-mrsid-image": {
+ "source": "apache",
+ "extensions": ["sid"]
+ },
+ "image/x-ms-bmp": {
+ "compressible": true,
+ "extensions": ["bmp"]
+ },
+ "image/x-pcx": {
+ "source": "apache",
+ "extensions": ["pcx"]
+ },
+ "image/x-pict": {
+ "source": "apache",
+ "extensions": ["pic","pct"]
+ },
+ "image/x-portable-anymap": {
+ "source": "apache",
+ "extensions": ["pnm"]
+ },
+ "image/x-portable-bitmap": {
+ "source": "apache",
+ "extensions": ["pbm"]
+ },
+ "image/x-portable-graymap": {
+ "source": "apache",
+ "extensions": ["pgm"]
+ },
+ "image/x-portable-pixmap": {
+ "source": "apache",
+ "extensions": ["ppm"]
+ },
+ "image/x-rgb": {
+ "source": "apache",
+ "extensions": ["rgb"]
+ },
+ "image/x-tga": {
+ "source": "apache",
+ "extensions": ["tga"]
+ },
+ "image/x-xbitmap": {
+ "source": "apache",
+ "extensions": ["xbm"]
+ },
+ "image/x-xcf": {
+ "compressible": false
+ },
+ "image/x-xpixmap": {
+ "source": "apache",
+ "extensions": ["xpm"]
+ },
+ "image/x-xwindowdump": {
+ "source": "apache",
+ "extensions": ["xwd"]
+ },
+ "message/cpim": {
+ "source": "iana"
+ },
+ "message/delivery-status": {
+ "source": "iana"
+ },
+ "message/disposition-notification": {
+ "source": "iana"
+ },
+ "message/external-body": {
+ "source": "iana"
+ },
+ "message/feedback-report": {
+ "source": "iana"
+ },
+ "message/global": {
+ "source": "iana"
+ },
+ "message/global-delivery-status": {
+ "source": "iana"
+ },
+ "message/global-disposition-notification": {
+ "source": "iana"
+ },
+ "message/global-headers": {
+ "source": "iana"
+ },
+ "message/http": {
+ "source": "iana",
+ "compressible": false
+ },
+ "message/imdn+xml": {
+ "source": "iana",
+ "compressible": true
+ },
+ "message/news": {
+ "source": "iana"
+ },
+ "message/partial": {
+ "source": "iana",
+ "compressible": false
+ },
+ "message/rfc822": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["eml","mime"]
+ },
+ "message/s-http": {
+ "source": "iana"
+ },
+ "message/sip": {
+ "source": "iana"
+ },
+ "message/sipfrag": {
+ "source": "iana"
+ },
+ "message/tracking-status": {
+ "source": "iana"
+ },
+ "message/vnd.si.simp": {
+ "source": "iana"
+ },
+ "message/vnd.wfa.wsc": {
+ "source": "iana"
+ },
+ "model/iges": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["igs","iges"]
+ },
+ "model/mesh": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["msh","mesh","silo"]
+ },
+ "model/vnd.collada+xml": {
+ "source": "iana",
+ "extensions": ["dae"]
+ },
+ "model/vnd.dwf": {
+ "source": "iana",
+ "extensions": ["dwf"]
+ },
+ "model/vnd.flatland.3dml": {
+ "source": "iana"
+ },
+ "model/vnd.gdl": {
+ "source": "iana",
+ "extensions": ["gdl"]
+ },
+ "model/vnd.gs-gdl": {
+ "source": "apache"
+ },
+ "model/vnd.gs.gdl": {
+ "source": "iana"
+ },
+ "model/vnd.gtw": {
+ "source": "iana",
+ "extensions": ["gtw"]
+ },
+ "model/vnd.moml+xml": {
+ "source": "iana"
+ },
+ "model/vnd.mts": {
+ "source": "iana",
+ "extensions": ["mts"]
+ },
+ "model/vnd.opengex": {
+ "source": "iana"
+ },
+ "model/vnd.parasolid.transmit.binary": {
+ "source": "iana"
+ },
+ "model/vnd.parasolid.transmit.text": {
+ "source": "iana"
+ },
+ "model/vnd.valve.source.compiled-map": {
+ "source": "iana"
+ },
+ "model/vnd.vtu": {
+ "source": "iana",
+ "extensions": ["vtu"]
+ },
+ "model/vrml": {
+ "source": "iana",
+ "compressible": false,
+ "extensions": ["wrl","vrml"]
+ },
+ "model/x3d+binary": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["x3db","x3dbz"]
+ },
+ "model/x3d+fastinfoset": {
+ "source": "iana"
+ },
+ "model/x3d+vrml": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["x3dv","x3dvz"]
+ },
+ "model/x3d+xml": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["x3d","x3dz"]
+ },
+ "model/x3d-vrml": {
+ "source": "iana"
+ },
+ "multipart/alternative": {
+ "source": "iana",
+ "compressible": false
+ },
+ "multipart/appledouble": {
+ "source": "iana"
+ },
+ "multipart/byteranges": {
+ "source": "iana"
+ },
+ "multipart/digest": {
+ "source": "iana"
+ },
+ "multipart/encrypted": {
+ "source": "iana",
+ "compressible": false
+ },
+ "multipart/form-data": {
+ "source": "iana",
+ "compressible": false
+ },
+ "multipart/header-set": {
+ "source": "iana"
+ },
+ "multipart/mixed": {
+ "source": "iana",
+ "compressible": false
+ },
+ "multipart/parallel": {
+ "source": "iana"
+ },
+ "multipart/related": {
+ "source": "iana",
+ "compressible": false
+ },
+ "multipart/report": {
+ "source": "iana"
+ },
+ "multipart/signed": {
+ "source": "iana",
+ "compressible": false
+ },
+ "multipart/voice-message": {
+ "source": "iana"
+ },
+ "multipart/x-mixed-replace": {
+ "source": "iana"
+ },
+ "text/1d-interleaved-parityfec": {
+ "source": "iana"
+ },
+ "text/cache-manifest": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["appcache","manifest"]
+ },
+ "text/calendar": {
+ "source": "iana",
+ "extensions": ["ics","ifb"]
+ },
+ "text/calender": {
+ "compressible": true
+ },
+ "text/cmd": {
+ "compressible": true
+ },
+ "text/coffeescript": {
+ "extensions": ["coffee"]
+ },
+ "text/css": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["css"]
+ },
+ "text/csv": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["csv"]
+ },
+ "text/csv-schema": {
+ "source": "iana"
+ },
+ "text/directory": {
+ "source": "iana"
+ },
+ "text/dns": {
+ "source": "iana"
+ },
+ "text/ecmascript": {
+ "source": "iana"
+ },
+ "text/encaprtp": {
+ "source": "iana"
+ },
+ "text/enriched": {
+ "source": "iana"
+ },
+ "text/fwdred": {
+ "source": "iana"
+ },
+ "text/grammar-ref-list": {
+ "source": "iana"
+ },
+ "text/hjson": {
+ "extensions": ["hjson"]
+ },
+ "text/html": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["html","htm"]
+ },
+ "text/jade": {
+ "extensions": ["jade"]
+ },
+ "text/javascript": {
+ "source": "iana",
+ "compressible": true
+ },
+ "text/jcr-cnd": {
+ "source": "iana"
+ },
+ "text/jsx": {
+ "compressible": true,
+ "extensions": ["jsx"]
+ },
+ "text/less": {
+ "extensions": ["less"]
+ },
+ "text/markdown": {
+ "source": "iana"
+ },
+ "text/mizar": {
+ "source": "iana"
+ },
+ "text/n3": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["n3"]
+ },
+ "text/parameters": {
+ "source": "iana"
+ },
+ "text/parityfec": {
+ "source": "iana"
+ },
+ "text/plain": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["txt","text","conf","def","list","log","in","ini"]
+ },
+ "text/provenance-notation": {
+ "source": "iana"
+ },
+ "text/prs.fallenstein.rst": {
+ "source": "iana"
+ },
+ "text/prs.lines.tag": {
+ "source": "iana",
+ "extensions": ["dsc"]
+ },
+ "text/raptorfec": {
+ "source": "iana"
+ },
+ "text/red": {
+ "source": "iana"
+ },
+ "text/rfc822-headers": {
+ "source": "iana"
+ },
+ "text/richtext": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["rtx"]
+ },
+ "text/rtf": {
+ "source": "iana"
+ },
+ "text/rtp-enc-aescm128": {
+ "source": "iana"
+ },
+ "text/rtploopback": {
+ "source": "iana"
+ },
+ "text/rtx": {
+ "source": "iana"
+ },
+ "text/sgml": {
+ "source": "iana",
+ "extensions": ["sgml","sgm"]
+ },
+ "text/stylus": {
+ "extensions": ["stylus","styl"]
+ },
+ "text/t140": {
+ "source": "iana"
+ },
+ "text/tab-separated-values": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["tsv"]
+ },
+ "text/troff": {
+ "source": "iana",
+ "extensions": ["t","tr","roff","man","me","ms"]
+ },
+ "text/turtle": {
+ "source": "iana",
+ "extensions": ["ttl"]
+ },
+ "text/ulpfec": {
+ "source": "iana"
+ },
+ "text/uri-list": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["uri","uris","urls"]
+ },
+ "text/vcard": {
+ "source": "iana",
+ "compressible": true,
+ "extensions": ["vcard"]
+ },
+ "text/vnd.a": {
+ "source": "iana"
+ },
+ "text/vnd.abc": {
+ "source": "iana"
+ },
+ "text/vnd.curl": {
+ "source": "iana",
+ "extensions": ["curl"]
+ },
+ "text/vnd.curl.dcurl": {
+ "source": "apache",
+ "extensions": ["dcurl"]
+ },
+ "text/vnd.curl.mcurl": {
+ "source": "apache",
+ "extensions": ["mcurl"]
+ },
+ "text/vnd.curl.scurl": {
+ "source": "apache",
+ "extensions": ["scurl"]
+ },
+ "text/vnd.debian.copyright": {
+ "source": "iana"
+ },
+ "text/vnd.dmclientscript": {
+ "source": "iana"
+ },
+ "text/vnd.dvb.subtitle": {
+ "source": "iana",
+ "extensions": ["sub"]
+ },
+ "text/vnd.esmertec.theme-descriptor": {
+ "source": "iana"
+ },
+ "text/vnd.fly": {
+ "source": "iana",
+ "extensions": ["fly"]
+ },
+ "text/vnd.fmi.flexstor": {
+ "source": "iana",
+ "extensions": ["flx"]
+ },
+ "text/vnd.graphviz": {
+ "source": "iana",
+ "extensions": ["gv"]
+ },
+ "text/vnd.in3d.3dml": {
+ "source": "iana",
+ "extensions": ["3dml"]
+ },
+ "text/vnd.in3d.spot": {
+ "source": "iana",
+ "extensions": ["spot"]
+ },
+ "text/vnd.iptc.newsml": {
+ "source": "iana"
+ },
+ "text/vnd.iptc.nitf": {
+ "source": "iana"
+ },
+ "text/vnd.latex-z": {
+ "source": "iana"
+ },
+ "text/vnd.motorola.reflex": {
+ "source": "iana"
+ },
+ "text/vnd.ms-mediapackage": {
+ "source": "iana"
+ },
+ "text/vnd.net2phone.commcenter.command": {
+ "source": "iana"
+ },
+ "text/vnd.radisys.msml-basic-layout": {
+ "source": "iana"
+ },
+ "text/vnd.si.uricatalogue": {
+ "source": "iana"
+ },
+ "text/vnd.sun.j2me.app-descriptor": {
+ "source": "iana",
+ "extensions": ["jad"]
+ },
+ "text/vnd.trolltech.linguist": {
+ "source": "iana"
+ },
+ "text/vnd.wap.si": {
+ "source": "iana"
+ },
+ "text/vnd.wap.sl": {
+ "source": "iana"
+ },
+ "text/vnd.wap.wml": {
+ "source": "iana",
+ "extensions": ["wml"]
+ },
+ "text/vnd.wap.wmlscript": {
+ "source": "iana",
+ "extensions": ["wmls"]
+ },
+ "text/vtt": {
+ "charset": "UTF-8",
+ "compressible": true,
+ "extensions": ["vtt"]
+ },
+ "text/x-asm": {
+ "source": "apache",
+ "extensions": ["s","asm"]
+ },
+ "text/x-c": {
+ "source": "apache",
+ "extensions": ["c","cc","cxx","cpp","h","hh","dic"]
+ },
+ "text/x-component": {
+ "extensions": ["htc"]
+ },
+ "text/x-fortran": {
+ "source": "apache",
+ "extensions": ["f","for","f77","f90"]
+ },
+ "text/x-gwt-rpc": {
+ "compressible": true
+ },
+ "text/x-handlebars-template": {
+ "extensions": ["hbs"]
+ },
+ "text/x-java-source": {
+ "source": "apache",
+ "extensions": ["java"]
+ },
+ "text/x-jquery-tmpl": {
+ "compressible": true
+ },
+ "text/x-lua": {
+ "extensions": ["lua"]
+ },
+ "text/x-markdown": {
+ "compressible": true,
+ "extensions": ["markdown","md","mkd"]
+ },
+ "text/x-nfo": {
+ "source": "apache",
+ "extensions": ["nfo"]
+ },
+ "text/x-opml": {
+ "source": "apache",
+ "extensions": ["opml"]
+ },
+ "text/x-pascal": {
+ "source": "apache",
+ "extensions": ["p","pas"]
+ },
+ "text/x-sass": {
+ "extensions": ["sass"]
+ },
+ "text/x-scss": {
+ "extensions": ["scss"]
+ },
+ "text/x-setext": {
+ "source": "apache",
+ "extensions": ["etx"]
+ },
+ "text/x-sfv": {
+ "source": "apache",
+ "extensions": ["sfv"]
+ },
+ "text/x-uuencode": {
+ "source": "apache",
+ "extensions": ["uu"]
+ },
+ "text/x-vcalendar": {
+ "source": "apache",
+ "extensions": ["vcs"]
+ },
+ "text/x-vcard": {
+ "source": "apache",
+ "extensions": ["vcf"]
+ },
+ "text/xml": {
+ "source": "iana",
+ "compressible": true
+ },
+ "text/xml-external-parsed-entity": {
+ "source": "iana"
+ },
+ "text/yaml": {
+ "extensions": ["yaml","yml"]
+ },
+ "video/1d-interleaved-parityfec": {
+ "source": "apache"
+ },
+ "video/3gpp": {
+ "source": "apache",
+ "extensions": ["3gp"]
+ },
+ "video/3gpp-tt": {
+ "source": "apache"
+ },
+ "video/3gpp2": {
+ "source": "apache",
+ "extensions": ["3g2"]
+ },
+ "video/bmpeg": {
+ "source": "apache"
+ },
+ "video/bt656": {
+ "source": "apache"
+ },
+ "video/celb": {
+ "source": "apache"
+ },
+ "video/dv": {
+ "source": "apache"
+ },
+ "video/h261": {
+ "source": "apache",
+ "extensions": ["h261"]
+ },
+ "video/h263": {
+ "source": "apache",
+ "extensions": ["h263"]
+ },
+ "video/h263-1998": {
+ "source": "apache"
+ },
+ "video/h263-2000": {
+ "source": "apache"
+ },
+ "video/h264": {
+ "source": "apache",
+ "extensions": ["h264"]
+ },
+ "video/h264-rcdo": {
+ "source": "apache"
+ },
+ "video/h264-svc": {
+ "source": "apache"
+ },
+ "video/jpeg": {
+ "source": "apache",
+ "extensions": ["jpgv"]
+ },
+ "video/jpeg2000": {
+ "source": "apache"
+ },
+ "video/jpm": {
+ "source": "apache",
+ "extensions": ["jpm","jpgm"]
+ },
+ "video/mj2": {
+ "source": "apache",
+ "extensions": ["mj2","mjp2"]
+ },
+ "video/mp1s": {
+ "source": "apache"
+ },
+ "video/mp2p": {
+ "source": "apache"
+ },
+ "video/mp2t": {
+ "source": "apache",
+ "extensions": ["ts"]
+ },
+ "video/mp4": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["mp4","mp4v","mpg4"]
+ },
+ "video/mp4v-es": {
+ "source": "apache"
+ },
+ "video/mpeg": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["mpeg","mpg","mpe","m1v","m2v"]
+ },
+ "video/mpeg4-generic": {
+ "source": "apache"
+ },
+ "video/mpv": {
+ "source": "apache"
+ },
+ "video/nv": {
+ "source": "apache"
+ },
+ "video/ogg": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["ogv"]
+ },
+ "video/parityfec": {
+ "source": "apache"
+ },
+ "video/pointer": {
+ "source": "apache"
+ },
+ "video/quicktime": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["qt","mov"]
+ },
+ "video/raw": {
+ "source": "apache"
+ },
+ "video/rtp-enc-aescm128": {
+ "source": "apache"
+ },
+ "video/rtx": {
+ "source": "apache"
+ },
+ "video/smpte292m": {
+ "source": "apache"
+ },
+ "video/ulpfec": {
+ "source": "apache"
+ },
+ "video/vc1": {
+ "source": "apache"
+ },
+ "video/vnd.cctv": {
+ "source": "apache"
+ },
+ "video/vnd.dece.hd": {
+ "source": "apache",
+ "extensions": ["uvh","uvvh"]
+ },
+ "video/vnd.dece.mobile": {
+ "source": "apache",
+ "extensions": ["uvm","uvvm"]
+ },
+ "video/vnd.dece.mp4": {
+ "source": "apache"
+ },
+ "video/vnd.dece.pd": {
+ "source": "apache",
+ "extensions": ["uvp","uvvp"]
+ },
+ "video/vnd.dece.sd": {
+ "source": "apache",
+ "extensions": ["uvs","uvvs"]
+ },
+ "video/vnd.dece.video": {
+ "source": "apache",
+ "extensions": ["uvv","uvvv"]
+ },
+ "video/vnd.directv.mpeg": {
+ "source": "apache"
+ },
+ "video/vnd.directv.mpeg-tts": {
+ "source": "apache"
+ },
+ "video/vnd.dlna.mpeg-tts": {
+ "source": "apache"
+ },
+ "video/vnd.dvb.file": {
+ "source": "apache",
+ "extensions": ["dvb"]
+ },
+ "video/vnd.fvt": {
+ "source": "apache",
+ "extensions": ["fvt"]
+ },
+ "video/vnd.hns.video": {
+ "source": "apache"
+ },
+ "video/vnd.iptvforum.1dparityfec-1010": {
+ "source": "apache"
+ },
+ "video/vnd.iptvforum.1dparityfec-2005": {
+ "source": "apache"
+ },
+ "video/vnd.iptvforum.2dparityfec-1010": {
+ "source": "apache"
+ },
+ "video/vnd.iptvforum.2dparityfec-2005": {
+ "source": "apache"
+ },
+ "video/vnd.iptvforum.ttsavc": {
+ "source": "apache"
+ },
+ "video/vnd.iptvforum.ttsmpeg2": {
+ "source": "apache"
+ },
+ "video/vnd.motorola.video": {
+ "source": "apache"
+ },
+ "video/vnd.motorola.videop": {
+ "source": "apache"
+ },
+ "video/vnd.mpegurl": {
+ "source": "apache",
+ "extensions": ["mxu","m4u"]
+ },
+ "video/vnd.ms-playready.media.pyv": {
+ "source": "apache",
+ "extensions": ["pyv"]
+ },
+ "video/vnd.nokia.interleaved-multimedia": {
+ "source": "apache"
+ },
+ "video/vnd.nokia.videovoip": {
+ "source": "apache"
+ },
+ "video/vnd.objectvideo": {
+ "source": "apache"
+ },
+ "video/vnd.sealed.mpeg1": {
+ "source": "apache"
+ },
+ "video/vnd.sealed.mpeg4": {
+ "source": "apache"
+ },
+ "video/vnd.sealed.swf": {
+ "source": "apache"
+ },
+ "video/vnd.sealedmedia.softseal.mov": {
+ "source": "apache"
+ },
+ "video/vnd.uvvu.mp4": {
+ "source": "apache",
+ "extensions": ["uvu","uvvu"]
+ },
+ "video/vnd.vivo": {
+ "source": "apache",
+ "extensions": ["viv"]
+ },
+ "video/webm": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["webm"]
+ },
+ "video/x-f4v": {
+ "source": "apache",
+ "extensions": ["f4v"]
+ },
+ "video/x-fli": {
+ "source": "apache",
+ "extensions": ["fli"]
+ },
+ "video/x-flv": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["flv"]
+ },
+ "video/x-m4v": {
+ "source": "apache",
+ "extensions": ["m4v"]
+ },
+ "video/x-matroska": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["mkv","mk3d","mks"]
+ },
+ "video/x-mng": {
+ "source": "apache",
+ "extensions": ["mng"]
+ },
+ "video/x-ms-asf": {
+ "source": "apache",
+ "extensions": ["asf","asx"]
+ },
+ "video/x-ms-vob": {
+ "source": "apache",
+ "extensions": ["vob"]
+ },
+ "video/x-ms-wm": {
+ "source": "apache",
+ "extensions": ["wm"]
+ },
+ "video/x-ms-wmv": {
+ "source": "apache",
+ "compressible": false,
+ "extensions": ["wmv"]
+ },
+ "video/x-ms-wmx": {
+ "source": "apache",
+ "extensions": ["wmx"]
+ },
+ "video/x-ms-wvx": {
+ "source": "apache",
+ "extensions": ["wvx"]
+ },
+ "video/x-msvideo": {
+ "source": "apache",
+ "extensions": ["avi"]
+ },
+ "video/x-sgi-movie": {
+ "source": "apache",
+ "extensions": ["movie"]
+ },
+ "video/x-smv": {
+ "source": "apache",
+ "extensions": ["smv"]
+ },
+ "x-conference/x-cooltalk": {
+ "source": "apache",
+ "extensions": ["ice"]
+ },
+ "x-shader/x-fragment": {
+ "compressible": true
+ },
+ "x-shader/x-vertex": {
+ "compressible": true
+ }
+}
--- /dev/null
+/*!
+ * mime-db
+ * Copyright(c) 2014 Jonathan Ong
+ * MIT Licensed
+ */
+
+/**
+ * Module exports.
+ */
+
+module.exports = require('./db.json')
--- /dev/null
+{
+ "name": "mime-db",
+ "description": "Media Type Database",
+ "version": "1.10.0",
+ "contributors": [
+ {
+ "name": "Douglas Christopher Wilson",
+ "email": "doug@somethingdoug.com"
+ },
+ {
+ "name": "Jonathan Ong",
+ "email": "me@jongleberry.com",
+ "url": "http://jongleberry.com"
+ },
+ {
+ "name": "Robert Kieffer",
+ "email": "robert@broofa.com",
+ "url": "http://github.com/broofa"
+ }
+ ],
+ "license": "MIT",
+ "keywords": [
+ "mime",
+ "db",
+ "type",
+ "types",
+ "database",
+ "charset",
+ "charsets"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jshttp/mime-db"
+ },
+ "devDependencies": {
+ "bluebird": "~2.9.20",
+ "co": "4.5.4",
+ "cogent": "1.0.1",
+ "csv-parse": "0.1.1",
+ "gnode": "0.1.1",
+ "istanbul": "0.3.9",
+ "mocha": "1.21.5",
+ "raw-body": "2.0.1",
+ "stream-to-array": "2"
+ },
+ "files": [
+ "HISTORY.md",
+ "LICENSE",
+ "README.md",
+ "db.json",
+ "index.js"
+ ],
+ "engines": {
+ "node": ">= 0.6"
+ },
+ "scripts": {
+ "build": "node scripts/build",
+ "fetch": "gnode scripts/extensions && gnode scripts/types",
+ "test": "mocha --reporter spec --bail --check-leaks test/",
+ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
+ "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/",
+ "update": "npm run fetch && npm run build"
+ },
+ "gitHead": "260552f9177fe78986b92699999f81999c7fe43c",
+ "bugs": {
+ "url": "https://github.com/jshttp/mime-db/issues"
+ },
+ "homepage": "https://github.com/jshttp/mime-db",
+ "_id": "mime-db@1.10.0",
+ "_shasum": "e6308063c758ebd12837874c3d1ea9170766b03b",
+ "_from": "mime-db@>=1.10.0 <1.11.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "dougwilson",
+ "email": "doug@somethingdoug.com"
+ },
+ "maintainers": [
+ {
+ "name": "jongleberry",
+ "email": "jonathanrichardong@gmail.com"
+ },
+ {
+ "name": "dougwilson",
+ "email": "doug@somethingdoug.com"
+ }
+ ],
+ "dist": {
+ "shasum": "e6308063c758ebd12837874c3d1ea9170766b03b",
+ "tarball": "http://registry.npmjs.org/mime-db/-/mime-db-1.10.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.10.0.tgz"
+}
--- /dev/null
+{
+ "name": "mime-types",
+ "description": "The ultimate javascript content-type utility.",
+ "version": "2.0.12",
+ "contributors": [
+ {
+ "name": "Douglas Christopher Wilson",
+ "email": "doug@somethingdoug.com"
+ },
+ {
+ "name": "Jeremiah Senkpiel",
+ "email": "fishrock123@rocketmail.com",
+ "url": "https://searchbeam.jit.su"
+ },
+ {
+ "name": "Jonathan Ong",
+ "email": "me@jongleberry.com",
+ "url": "http://jongleberry.com"
+ }
+ ],
+ "license": "MIT",
+ "keywords": [
+ "mime",
+ "types"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jshttp/mime-types"
+ },
+ "dependencies": {
+ "mime-db": "~1.10.0"
+ },
+ "devDependencies": {
+ "istanbul": "0.3.9",
+ "mocha": "~1.21.5"
+ },
+ "files": [
+ "HISTORY.md",
+ "LICENSE",
+ "index.js"
+ ],
+ "engines": {
+ "node": ">= 0.6"
+ },
+ "scripts": {
+ "test": "mocha --reporter spec test/test.js",
+ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/test.js",
+ "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot test/test.js"
+ },
+ "gitHead": "a1e3c4b6bbb6afb615c8a058481d58cb57a4cb95",
+ "bugs": {
+ "url": "https://github.com/jshttp/mime-types/issues"
+ },
+ "homepage": "https://github.com/jshttp/mime-types",
+ "_id": "mime-types@2.0.12",
+ "_shasum": "87ae9f124e94f8e440c93d1a72d0dccecdb71135",
+ "_from": "mime-types@>=2.0.1 <2.1.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "dougwilson",
+ "email": "doug@somethingdoug.com"
+ },
+ "maintainers": [
+ {
+ "name": "jongleberry",
+ "email": "jonathanrichardong@gmail.com"
+ },
+ {
+ "name": "fishrock123",
+ "email": "fishrock123@rocketmail.com"
+ },
+ {
+ "name": "dougwilson",
+ "email": "doug@somethingdoug.com"
+ }
+ ],
+ "dist": {
+ "shasum": "87ae9f124e94f8e440c93d1a72d0dccecdb71135",
+ "tarball": "http://registry.npmjs.org/mime-types/-/mime-types-2.0.12.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.12.tgz"
+}
--- /dev/null
+node_modules
+.DS_Store
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2010-2012 Robert Kieffer
+
+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.
--- /dev/null
+# node-uuid
+
+Simple, fast generation of [RFC4122](http://www.ietf.org/rfc/rfc4122.txt) UUIDS.
+
+Features:
+
+* Generate RFC4122 version 1 or version 4 UUIDs
+* Runs in node.js and all browsers.
+* Registered as a [ComponentJS](https://github.com/component/component) [component](https://github.com/component/component/wiki/Components) ('broofa/node-uuid').
+* Cryptographically strong random # generation on supporting platforms
+* 1.1K minified and gzip'ed (Want something smaller? Check this [crazy shit](https://gist.github.com/982883) out! )
+* [Annotated source code](http://broofa.github.com/node-uuid/docs/uuid.html)
+* Comes with a Command Line Interface for generating uuids on the command line
+
+## Getting Started
+
+Install it in your browser:
+
+```html
+<script src="uuid.js"></script>
+```
+
+Or in node.js:
+
+```
+npm install node-uuid
+```
+
+```javascript
+var uuid = require('node-uuid');
+```
+
+Then create some ids ...
+
+```javascript
+// Generate a v1 (time-based) id
+uuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
+
+// Generate a v4 (random) id
+uuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'
+```
+
+## API
+
+### uuid.v1([`options` [, `buffer` [, `offset`]]])
+
+Generate and return a RFC4122 v1 (timestamp-based) UUID.
+
+* `options` - (Object) Optional uuid state to apply. Properties may include:
+
+ * `node` - (Array) Node id as Array of 6 bytes (per 4.1.6). Default: Randomly generated ID. See note 1.
+ * `clockseq` - (Number between 0 - 0x3fff) RFC clock sequence. Default: An internally maintained clockseq is used.
+ * `msecs` - (Number | Date) Time in milliseconds since unix Epoch. Default: The current time is used.
+ * `nsecs` - (Number between 0-9999) additional time, in 100-nanosecond units. Ignored if `msecs` is unspecified. Default: internal uuid counter is used, as per 4.2.1.2.
+
+* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written.
+* `offset` - (Number) Starting index in `buffer` at which to begin writing.
+
+Returns `buffer`, if specified, otherwise the string form of the UUID
+
+Notes:
+
+1. The randomly generated node id is only guaranteed to stay constant for the lifetime of the current JS runtime. (Future versions of this module may use persistent storage mechanisms to extend this guarantee.)
+
+Example: Generate string UUID with fully-specified options
+
+```javascript
+uuid.v1({
+ node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
+ clockseq: 0x1234,
+ msecs: new Date('2011-11-01').getTime(),
+ nsecs: 5678
+}); // -> "710b962e-041c-11e1-9234-0123456789ab"
+```
+
+Example: In-place generation of two binary IDs
+
+```javascript
+// Generate two ids in an array
+var arr = new Array(32); // -> []
+uuid.v1(null, arr, 0); // -> [02 a2 ce 90 14 32 11 e1 85 58 0b 48 8e 4f c1 15]
+uuid.v1(null, arr, 16); // -> [02 a2 ce 90 14 32 11 e1 85 58 0b 48 8e 4f c1 15 02 a3 1c b0 14 32 11 e1 85 58 0b 48 8e 4f c1 15]
+
+// Optionally use uuid.unparse() to get stringify the ids
+uuid.unparse(buffer); // -> '02a2ce90-1432-11e1-8558-0b488e4fc115'
+uuid.unparse(buffer, 16) // -> '02a31cb0-1432-11e1-8558-0b488e4fc115'
+```
+
+### uuid.v4([`options` [, `buffer` [, `offset`]]])
+
+Generate and return a RFC4122 v4 UUID.
+
+* `options` - (Object) Optional uuid state to apply. Properties may include:
+
+ * `random` - (Number[16]) Array of 16 numbers (0-255) to use in place of randomly generated values
+ * `rng` - (Function) Random # generator to use. Set to one of the built-in generators - `uuid.mathRNG` (all platforms), `uuid.nodeRNG` (node.js only), `uuid.whatwgRNG` (WebKit only) - or a custom function that returns an array[16] of byte values.
+
+* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written.
+* `offset` - (Number) Starting index in `buffer` at which to begin writing.
+
+Returns `buffer`, if specified, otherwise the string form of the UUID
+
+Example: Generate string UUID with fully-specified options
+
+```javascript
+uuid.v4({
+ random: [
+ 0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0xc1, 0xea,
+ 0x71, 0xb4, 0xef, 0xe1, 0x67, 0x1c, 0x58, 0x36
+ ]
+});
+// -> "109156be-c4fb-41ea-b1b4-efe1671c5836"
+```
+
+Example: Generate two IDs in a single buffer
+
+```javascript
+var buffer = new Array(32); // (or 'new Buffer' in node.js)
+uuid.v4(null, buffer, 0);
+uuid.v4(null, buffer, 16);
+```
+
+### uuid.parse(id[, buffer[, offset]])
+### uuid.unparse(buffer[, offset])
+
+Parse and unparse UUIDs
+
+ * `id` - (String) UUID(-like) string
+ * `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written. Default: A new Array or Buffer is used
+ * `offset` - (Number) Starting index in `buffer` at which to begin writing. Default: 0
+
+Example parsing and unparsing a UUID string
+
+```javascript
+var bytes = uuid.parse('797ff043-11eb-11e1-80d6-510998755d10'); // -> <Buffer 79 7f f0 43 11 eb 11 e1 80 d6 51 09 98 75 5d 10>
+var string = uuid.unparse(bytes); // -> '797ff043-11eb-11e1-80d6-510998755d10'
+```
+
+### uuid.noConflict()
+
+(Browsers only) Set `uuid` property back to it's previous value.
+
+Returns the node-uuid object.
+
+Example:
+
+```javascript
+var myUuid = uuid.noConflict();
+myUuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
+```
+
+## Deprecated APIs
+
+Support for the following v1.2 APIs is available in v1.3, but is deprecated and will be removed in the next major version.
+
+### uuid([format [, buffer [, offset]]])
+
+uuid() has become uuid.v4(), and the `format` argument is now implicit in the `buffer` argument. (i.e. if you specify a buffer, the format is assumed to be binary).
+
+### uuid.BufferClass
+
+The class of container created when generating binary uuid data if no buffer argument is specified. This is expected to go away, with no replacement API.
+
+## Command Line Interface
+
+To use the executable, it's probably best to install this library globally.
+
+`npm install -g node-uuid`
+
+Usage:
+
+```
+USAGE: uuid [version] [options]
+
+
+options:
+
+--help Display this message and exit
+```
+
+`version` must be an RFC4122 version that is supported by this library, which is currently version 1 and version 4 (denoted by "v1" and "v4", respectively). `version` defaults to version 4 when not supplied.
+
+### Examples
+
+```
+> uuid
+3a91f950-dec8-4688-ba14-5b7bbfc7a563
+```
+
+```
+> uuid v1
+9d0b43e0-7696-11e3-964b-250efa37a98e
+```
+
+```
+> uuid v4
+6790ac7c-24ac-4f98-8464-42f6d98a53ae
+```
+
+## Testing
+
+In node.js
+
+```
+npm test
+```
+
+In Browser
+
+```
+open test/test.html
+```
+
+### Benchmarking
+
+Requires node.js
+
+```
+npm install uuid uuid-js
+node benchmark/benchmark.js
+```
+
+For a more complete discussion of node-uuid performance, please see the `benchmark/README.md` file, and the [benchmark wiki](https://github.com/broofa/node-uuid/wiki/Benchmark)
+
+For browser performance [checkout the JSPerf tests](http://jsperf.com/node-uuid-performance).
+
+## Release notes
+
+### 1.4.0
+
+* Improved module context detection
+* Removed public RNG functions
+
+### 1.3.2
+
+* Improve tests and handling of v1() options (Issue #24)
+* Expose RNG option to allow for perf testing with different generators
+
+### 1.3.0
+
+* Support for version 1 ids, thanks to [@ctavan](https://github.com/ctavan)!
+* Support for node.js crypto API
+* De-emphasizing performance in favor of a) cryptographic quality PRNGs where available and b) more manageable code
--- /dev/null
+# node-uuid Benchmarks
+
+### Results
+
+To see the results of our benchmarks visit https://github.com/broofa/node-uuid/wiki/Benchmark
+
+### Run them yourself
+
+node-uuid comes with some benchmarks to measure performance of generating UUIDs. These can be run using node.js. node-uuid is being benchmarked against some other uuid modules, that are available through npm namely `uuid` and `uuid-js`.
+
+To prepare and run the benchmark issue;
+
+```
+npm install uuid uuid-js
+node benchmark/benchmark.js
+```
+
+You'll see an output like this one:
+
+```
+# v4
+nodeuuid.v4(): 854700 uuids/second
+nodeuuid.v4('binary'): 788643 uuids/second
+nodeuuid.v4('binary', buffer): 1336898 uuids/second
+uuid(): 479386 uuids/second
+uuid('binary'): 582072 uuids/second
+uuidjs.create(4): 312304 uuids/second
+
+# v1
+nodeuuid.v1(): 938086 uuids/second
+nodeuuid.v1('binary'): 683060 uuids/second
+nodeuuid.v1('binary', buffer): 1644736 uuids/second
+uuidjs.create(1): 190621 uuids/second
+```
+
+* The `uuid()` entries are for Nikhil Marathe's [uuid module](https://bitbucket.org/nikhilm/uuidjs) which is a wrapper around the native libuuid library.
+* The `uuidjs()` entries are for Patrick Negri's [uuid-js module](https://github.com/pnegri/uuid-js) which is a pure javascript implementation based on [UUID.js](https://github.com/LiosK/UUID.js) by LiosK.
+
+If you want to get more reliable results you can run the benchmark multiple times and write the output into a log file:
+
+```
+for i in {0..9}; do node benchmark/benchmark.js >> benchmark/bench_0.4.12.log; done;
+```
+
+If you're interested in how performance varies between different node versions, you can issue the above command multiple times.
+
+You can then use the shell script `bench.sh` provided in this directory to calculate the averages over all benchmark runs and draw a nice plot:
+
+```
+(cd benchmark/ && ./bench.sh)
+```
+
+This assumes you have [gnuplot](http://www.gnuplot.info/) and [ImageMagick](http://www.imagemagick.org/) installed. You'll find a nice `bench.png` graph in the `benchmark/` directory then.
--- /dev/null
+#!/opt/local/bin/gnuplot -persist
+#
+#
+# G N U P L O T
+# Version 4.4 patchlevel 3
+# last modified March 2011
+# System: Darwin 10.8.0
+#
+# Copyright (C) 1986-1993, 1998, 2004, 2007-2010
+# Thomas Williams, Colin Kelley and many others
+#
+# gnuplot home: http://www.gnuplot.info
+# faq, bugs, etc: type "help seeking-assistance"
+# immediate help: type "help"
+# plot window: hit 'h'
+set terminal postscript eps noenhanced defaultplex \
+ leveldefault color colortext \
+ solid linewidth 1.2 butt noclip \
+ palfuncparam 2000,0.003 \
+ "Helvetica" 14
+set output 'bench.eps'
+unset clip points
+set clip one
+unset clip two
+set bar 1.000000 front
+set border 31 front linetype -1 linewidth 1.000
+set xdata
+set ydata
+set zdata
+set x2data
+set y2data
+set timefmt x "%d/%m/%y,%H:%M"
+set timefmt y "%d/%m/%y,%H:%M"
+set timefmt z "%d/%m/%y,%H:%M"
+set timefmt x2 "%d/%m/%y,%H:%M"
+set timefmt y2 "%d/%m/%y,%H:%M"
+set timefmt cb "%d/%m/%y,%H:%M"
+set boxwidth
+set style fill empty border
+set style rectangle back fc lt -3 fillstyle solid 1.00 border lt -1
+set style circle radius graph 0.02, first 0, 0
+set dummy x,y
+set format x "% g"
+set format y "% g"
+set format x2 "% g"
+set format y2 "% g"
+set format z "% g"
+set format cb "% g"
+set angles radians
+unset grid
+set key title ""
+set key outside left top horizontal Right noreverse enhanced autotitles columnhead nobox
+set key noinvert samplen 4 spacing 1 width 0 height 0
+set key maxcolumns 2 maxrows 0
+unset label
+unset arrow
+set style increment default
+unset style line
+set style line 1 linetype 1 linewidth 2.000 pointtype 1 pointsize default pointinterval 0
+unset style arrow
+set style histogram clustered gap 2 title offset character 0, 0, 0
+unset logscale
+set offsets graph 0.05, 0.15, 0, 0
+set pointsize 1.5
+set pointintervalbox 1
+set encoding default
+unset polar
+unset parametric
+unset decimalsign
+set view 60, 30, 1, 1
+set samples 100, 100
+set isosamples 10, 10
+set surface
+unset contour
+set clabel '%8.3g'
+set mapping cartesian
+set datafile separator whitespace
+unset hidden3d
+set cntrparam order 4
+set cntrparam linear
+set cntrparam levels auto 5
+set cntrparam points 5
+set size ratio 0 1,1
+set origin 0,0
+set style data points
+set style function lines
+set xzeroaxis linetype -2 linewidth 1.000
+set yzeroaxis linetype -2 linewidth 1.000
+set zzeroaxis linetype -2 linewidth 1.000
+set x2zeroaxis linetype -2 linewidth 1.000
+set y2zeroaxis linetype -2 linewidth 1.000
+set ticslevel 0.5
+set mxtics default
+set mytics default
+set mztics default
+set mx2tics default
+set my2tics default
+set mcbtics default
+set xtics border in scale 1,0.5 mirror norotate offset character 0, 0, 0
+set xtics norangelimit
+set xtics ()
+set ytics border in scale 1,0.5 mirror norotate offset character 0, 0, 0
+set ytics autofreq norangelimit
+set ztics border in scale 1,0.5 nomirror norotate offset character 0, 0, 0
+set ztics autofreq norangelimit
+set nox2tics
+set noy2tics
+set cbtics border in scale 1,0.5 mirror norotate offset character 0, 0, 0
+set cbtics autofreq norangelimit
+set title ""
+set title offset character 0, 0, 0 font "" norotate
+set timestamp bottom
+set timestamp ""
+set timestamp offset character 0, 0, 0 font "" norotate
+set rrange [ * : * ] noreverse nowriteback # (currently [8.98847e+307:-8.98847e+307] )
+set autoscale rfixmin
+set autoscale rfixmax
+set trange [ * : * ] noreverse nowriteback # (currently [-5.00000:5.00000] )
+set autoscale tfixmin
+set autoscale tfixmax
+set urange [ * : * ] noreverse nowriteback # (currently [-10.0000:10.0000] )
+set autoscale ufixmin
+set autoscale ufixmax
+set vrange [ * : * ] noreverse nowriteback # (currently [-10.0000:10.0000] )
+set autoscale vfixmin
+set autoscale vfixmax
+set xlabel ""
+set xlabel offset character 0, 0, 0 font "" textcolor lt -1 norotate
+set x2label ""
+set x2label offset character 0, 0, 0 font "" textcolor lt -1 norotate
+set xrange [ * : * ] noreverse nowriteback # (currently [-0.150000:3.15000] )
+set autoscale xfixmin
+set autoscale xfixmax
+set x2range [ * : * ] noreverse nowriteback # (currently [0.00000:3.00000] )
+set autoscale x2fixmin
+set autoscale x2fixmax
+set ylabel ""
+set ylabel offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
+set y2label ""
+set y2label offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
+set yrange [ 0.00000 : 1.90000e+06 ] noreverse nowriteback # (currently [:] )
+set autoscale yfixmin
+set autoscale yfixmax
+set y2range [ * : * ] noreverse nowriteback # (currently [0.00000:1.90000e+06] )
+set autoscale y2fixmin
+set autoscale y2fixmax
+set zlabel ""
+set zlabel offset character 0, 0, 0 font "" textcolor lt -1 norotate
+set zrange [ * : * ] noreverse nowriteback # (currently [-10.0000:10.0000] )
+set autoscale zfixmin
+set autoscale zfixmax
+set cblabel ""
+set cblabel offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
+set cbrange [ * : * ] noreverse nowriteback # (currently [8.98847e+307:-8.98847e+307] )
+set autoscale cbfixmin
+set autoscale cbfixmax
+set zero 1e-08
+set lmargin -1
+set bmargin -1
+set rmargin -1
+set tmargin -1
+set pm3d explicit at s
+set pm3d scansautomatic
+set pm3d interpolate 1,1 flush begin noftriangles nohidden3d corners2color mean
+set palette positive nops_allcF maxcolors 0 gamma 1.5 color model RGB
+set palette rgbformulae 7, 5, 15
+set colorbox default
+set colorbox vertical origin screen 0.9, 0.2, 0 size screen 0.05, 0.6, 0 front bdefault
+set loadpath
+set fontpath
+set fit noerrorvariables
+GNUTERM = "aqua"
+plot 'bench_results.txt' using 2:xticlabel(1) w lp lw 2, '' using 3:xticlabel(1) w lp lw 2, '' using 4:xticlabel(1) w lp lw 2, '' using 5:xticlabel(1) w lp lw 2, '' using 6:xticlabel(1) w lp lw 2, '' using 7:xticlabel(1) w lp lw 2, '' using 8:xticlabel(1) w lp lw 2, '' using 9:xticlabel(1) w lp lw 2
+# EOF
--- /dev/null
+#!/bin/bash
+
+# for a given node version run:
+# for i in {0..9}; do node benchmark.js >> bench_0.6.2.log; done;
+
+PATTERNS=('nodeuuid.v1()' "nodeuuid.v1('binary'," 'nodeuuid.v4()' "nodeuuid.v4('binary'," "uuid()" "uuid('binary')" 'uuidjs.create(1)' 'uuidjs.create(4)' '140byte')
+FILES=(node_uuid_v1_string node_uuid_v1_buf node_uuid_v4_string node_uuid_v4_buf libuuid_v4_string libuuid_v4_binary uuidjs_v1_string uuidjs_v4_string 140byte_es)
+INDICES=(2 3 2 3 2 2 2 2 2)
+VERSIONS=$( ls bench_*.log | sed -e 's/^bench_\([0-9\.]*\)\.log/\1/' | tr "\\n" " " )
+TMPJOIN="tmp_join"
+OUTPUT="bench_results.txt"
+
+for I in ${!FILES[*]}; do
+ F=${FILES[$I]}
+ P=${PATTERNS[$I]}
+ INDEX=${INDICES[$I]}
+ echo "version $F" > $F
+ for V in $VERSIONS; do
+ (VAL=$( grep "$P" bench_$V.log | LC_ALL=en_US awk '{ sum += $'$INDEX' } END { print sum/NR }' ); echo $V $VAL) >> $F
+ done
+ if [ $I == 0 ]; then
+ cat $F > $TMPJOIN
+ else
+ join $TMPJOIN $F > $OUTPUT
+ cp $OUTPUT $TMPJOIN
+ fi
+ rm $F
+done
+
+rm $TMPJOIN
+
+gnuplot bench.gnu
+convert -density 200 -resize 800x560 -flatten bench.eps bench.png
+rm bench.eps
--- /dev/null
+/*
+Test performance of native C UUID generation
+
+To Compile: cc -luuid benchmark-native.c -o benchmark-native
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <uuid/uuid.h>
+
+int main() {
+ uuid_t myid;
+ char buf[36+1];
+ int i;
+ struct timeval t;
+ double start, finish;
+
+ gettimeofday(&t, NULL);
+ start = t.tv_sec + t.tv_usec/1e6;
+
+ int n = 2e5;
+ for (i = 0; i < n; i++) {
+ uuid_generate(myid);
+ uuid_unparse(myid, buf);
+ }
+
+ gettimeofday(&t, NULL);
+ finish = t.tv_sec + t.tv_usec/1e6;
+ double dur = finish - start;
+
+ printf("%d uuids/sec", (int)(n/dur));
+ return 0;
+}
--- /dev/null
+try {
+ var nodeuuid = require('../uuid');
+} catch (e) {
+ console.error('node-uuid require failed - skipping tests');
+}
+
+try {
+ var uuid = require('uuid');
+} catch (e) {
+ console.error('uuid require failed - skipping tests');
+}
+
+try {
+ var uuidjs = require('uuid-js');
+} catch (e) {
+ console.error('uuid-js require failed - skipping tests');
+}
+
+var N = 5e5;
+
+function rate(msg, t) {
+ console.log(msg + ': ' +
+ (N / (Date.now() - t) * 1e3 | 0) +
+ ' uuids/second');
+}
+
+console.log('# v4');
+
+// node-uuid - string form
+if (nodeuuid) {
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4();
+ rate('nodeuuid.v4() - using node.js crypto RNG', t);
+
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4({rng: nodeuuid.mathRNG});
+ rate('nodeuuid.v4() - using Math.random() RNG', t);
+
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4('binary');
+ rate('nodeuuid.v4(\'binary\')', t);
+
+ var buffer = new nodeuuid.BufferClass(16);
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4('binary', buffer);
+ rate('nodeuuid.v4(\'binary\', buffer)', t);
+}
+
+// libuuid - string form
+if (uuid) {
+ for (var i = 0, t = Date.now(); i < N; i++) uuid();
+ rate('uuid()', t);
+
+ for (var i = 0, t = Date.now(); i < N; i++) uuid('binary');
+ rate('uuid(\'binary\')', t);
+}
+
+// uuid-js - string form
+if (uuidjs) {
+ for (var i = 0, t = Date.now(); i < N; i++) uuidjs.create(4);
+ rate('uuidjs.create(4)', t);
+}
+
+// 140byte.es
+for (var i = 0, t = Date.now(); i < N; i++) 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(s,r){r=Math.random()*16|0;return (s=='x'?r:r&0x3|0x8).toString(16)});
+rate('140byte.es_v4', t);
+
+console.log('');
+console.log('# v1');
+
+// node-uuid - v1 string form
+if (nodeuuid) {
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v1();
+ rate('nodeuuid.v1()', t);
+
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v1('binary');
+ rate('nodeuuid.v1(\'binary\')', t);
+
+ var buffer = new nodeuuid.BufferClass(16);
+ for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v1('binary', buffer);
+ rate('nodeuuid.v1(\'binary\', buffer)', t);
+}
+
+// uuid-js - v1 string form
+if (uuidjs) {
+ for (var i = 0, t = Date.now(); i < N; i++) uuidjs.create(1);
+ rate('uuidjs.create(1)', t);
+}
--- /dev/null
+#!/usr/bin/env node
+
+var path = require('path');
+var uuid = require(path.join(__dirname, '..'));
+
+var arg = process.argv[2];
+
+if ('--help' === arg) {
+ console.log('\n USAGE: uuid [version] [options]\n\n');
+ console.log(' options:\n');
+ console.log(' --help Display this message and exit\n');
+ process.exit(0);
+}
+
+if (null == arg) {
+ console.log(uuid());
+ process.exit(0);
+}
+
+if ('v1' !== arg && 'v4' !== arg) {
+ console.error('Version must be RFC4122 version 1 or version 4, denoted as "v1" or "v4"');
+ process.exit(1);
+}
+
+console.log(uuid[arg]());
+process.exit(0);
--- /dev/null
+{
+ "name": "node-uuid",
+ "version": "1.4.3",
+ "homepage": "https://github.com/broofa/node-uuid",
+ "authors": [
+ "Robert Kieffer <robert@broofa.com>"
+ ],
+ "description": "Rigorous implementation of RFC4122 (v1 and v4) UUIDs.",
+ "main": "uuid.js",
+ "keywords": [
+ "uuid",
+ "gid",
+ "rfc4122"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}
--- /dev/null
+{
+ "name": "node-uuid",
+ "repo": "broofa/node-uuid",
+ "description": "Rigorous implementation of RFC4122 (v1 and v4) UUIDs.",
+ "version": "1.4.3",
+ "author": "Robert Kieffer <robert@broofa.com>",
+ "contributors": [
+ {"name": "Christoph Tavan <dev@tavan.de>", "github": "https://github.com/ctavan"}
+ ],
+ "keywords": ["uuid", "guid", "rfc4122"],
+ "dependencies": {},
+ "development": {},
+ "main": "uuid.js",
+ "scripts": [
+ "uuid.js"
+ ],
+ "license": "MIT"
+}
--- /dev/null
+{
+ "name": "node-uuid",
+ "description": "Rigorous implementation of RFC4122 (v1 and v4) UUIDs.",
+ "url": "http://github.com/broofa/node-uuid",
+ "keywords": [
+ "uuid",
+ "guid",
+ "rfc4122"
+ ],
+ "author": {
+ "name": "Robert Kieffer",
+ "email": "robert@broofa.com"
+ },
+ "contributors": [
+ {
+ "name": "Christoph Tavan",
+ "email": "dev@tavan.de"
+ }
+ ],
+ "bin": {
+ "uuid": "./bin/uuid"
+ },
+ "scripts": {
+ "test": "node test/test.js"
+ },
+ "lib": ".",
+ "main": "./uuid.js",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/broofa/node-uuid.git"
+ },
+ "version": "1.4.3",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://raw.github.com/broofa/node-uuid/master/LICENSE.md"
+ }
+ ],
+ "gitHead": "886463c660a095dfebfa69603921a8d156fdb12c",
+ "bugs": {
+ "url": "https://github.com/broofa/node-uuid/issues"
+ },
+ "homepage": "https://github.com/broofa/node-uuid",
+ "_id": "node-uuid@1.4.3",
+ "_shasum": "319bb7a56e7cb63f00b5c0cd7851cd4b4ddf1df9",
+ "_from": "node-uuid@>=1.4.0 <1.5.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "broofa",
+ "email": "robert@broofa.com"
+ },
+ "maintainers": [
+ {
+ "name": "broofa",
+ "email": "robert@broofa.com"
+ }
+ ],
+ "dist": {
+ "shasum": "319bb7a56e7cb63f00b5c0cd7851cd4b4ddf1df9",
+ "tarball": "http://registry.npmjs.org/node-uuid/-/node-uuid-1.4.3.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.3.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+var assert = require('assert'),
+ nodeuuid = require('../uuid'),
+ uuidjs = require('uuid-js'),
+ libuuid = require('uuid').generate,
+ util = require('util'),
+ exec = require('child_process').exec,
+ os = require('os');
+
+// On Mac Os X / macports there's only the ossp-uuid package that provides uuid
+// On Linux there's uuid-runtime which provides uuidgen
+var uuidCmd = os.type() === 'Darwin' ? 'uuid -1' : 'uuidgen -t';
+
+function compare(ids) {
+ console.log(ids);
+ for (var i = 0; i < ids.length; i++) {
+ var id = ids[i].split('-');
+ id = [id[2], id[1], id[0]].join('');
+ ids[i] = id;
+ }
+ var sorted = ([].concat(ids)).sort();
+
+ if (sorted.toString() !== ids.toString()) {
+ console.log('Warning: sorted !== ids');
+ } else {
+ console.log('everything in order!');
+ }
+}
+
+// Test time order of v1 uuids
+var ids = [];
+while (ids.length < 10e3) ids.push(nodeuuid.v1());
+
+var max = 10;
+console.log('node-uuid:');
+ids = [];
+for (var i = 0; i < max; i++) ids.push(nodeuuid.v1());
+compare(ids);
+
+console.log('');
+console.log('uuidjs:');
+ids = [];
+for (var i = 0; i < max; i++) ids.push(uuidjs.create(1).toString());
+compare(ids);
+
+console.log('');
+console.log('libuuid:');
+ids = [];
+var count = 0;
+var last = function() {
+ compare(ids);
+}
+var cb = function(err, stdout, stderr) {
+ ids.push(stdout.substring(0, stdout.length-1));
+ count++;
+ if (count < max) {
+ return next();
+ }
+ last();
+};
+var next = function() {
+ exec(uuidCmd, cb);
+};
+next();
--- /dev/null
+<html>
+ <head>
+ <style>
+ div {
+ font-family: monospace;
+ font-size: 8pt;
+ }
+ div.log {color: #444;}
+ div.warn {color: #550;}
+ div.error {color: #800; font-weight: bold;}
+ </style>
+ <script src="../uuid.js"></script>
+ </head>
+ <body>
+ <script src="./test.js"></script>
+ </body>
+</html>
--- /dev/null
+if (!this.uuid) {
+ // node.js
+ uuid = require('../uuid');
+}
+
+//
+// x-platform log/assert shims
+//
+
+function _log(msg, type) {
+ type = type || 'log';
+
+ if (typeof(document) != 'undefined') {
+ document.write('<div class="' + type + '">' + msg.replace(/\n/g, '<br />') + '</div>');
+ }
+ if (typeof(console) != 'undefined') {
+ var color = {
+ log: '\033[39m',
+ warn: '\033[33m',
+ error: '\033[31m'
+ };
+ console[type](color[type] + msg + color.log);
+ }
+}
+
+function log(msg) {_log(msg, 'log');}
+function warn(msg) {_log(msg, 'warn');}
+function error(msg) {_log(msg, 'error');}
+
+function assert(res, msg) {
+ if (!res) {
+ error('FAIL: ' + msg);
+ } else {
+ log('Pass: ' + msg);
+ }
+}
+
+//
+// Unit tests
+//
+
+// Verify ordering of v1 ids created with explicit times
+var TIME = 1321644961388; // 2011-11-18 11:36:01.388-08:00
+
+function compare(name, ids) {
+ ids = ids.map(function(id) {
+ return id.split('-').reverse().join('-');
+ }).sort();
+ var sorted = ([].concat(ids)).sort();
+
+ assert(sorted.toString() == ids.toString(), name + ' have expected order');
+}
+
+// Verify ordering of v1 ids created using default behavior
+compare('uuids with current time', [
+ uuid.v1(),
+ uuid.v1(),
+ uuid.v1(),
+ uuid.v1(),
+ uuid.v1()
+]);
+
+// Verify ordering of v1 ids created with explicit times
+compare('uuids with time option', [
+ uuid.v1({msecs: TIME - 10*3600*1000}),
+ uuid.v1({msecs: TIME - 1}),
+ uuid.v1({msecs: TIME}),
+ uuid.v1({msecs: TIME + 1}),
+ uuid.v1({msecs: TIME + 28*24*3600*1000})
+]);
+
+assert(
+ uuid.v1({msecs: TIME}) != uuid.v1({msecs: TIME}),
+ 'IDs created at same msec are different'
+);
+
+// Verify throw if too many ids created
+var thrown = false;
+try {
+ uuid.v1({msecs: TIME, nsecs: 10000});
+} catch (e) {
+ thrown = true;
+}
+assert(thrown, 'Exception thrown when > 10K ids created in 1 ms');
+
+// Verify clock regression bumps clockseq
+var uidt = uuid.v1({msecs: TIME});
+var uidtb = uuid.v1({msecs: TIME - 1});
+assert(
+ parseInt(uidtb.split('-')[3], 16) - parseInt(uidt.split('-')[3], 16) === 1,
+ 'Clock regression by msec increments the clockseq'
+);
+
+// Verify clock regression bumps clockseq
+var uidtn = uuid.v1({msecs: TIME, nsecs: 10});
+var uidtnb = uuid.v1({msecs: TIME, nsecs: 9});
+assert(
+ parseInt(uidtnb.split('-')[3], 16) - parseInt(uidtn.split('-')[3], 16) === 1,
+ 'Clock regression by nsec increments the clockseq'
+);
+
+// Verify explicit options produce expected id
+var id = uuid.v1({
+ msecs: 1321651533573,
+ nsecs: 5432,
+ clockseq: 0x385c,
+ node: [ 0x61, 0xcd, 0x3c, 0xbb, 0x32, 0x10 ]
+});
+assert(id == 'd9428888-122b-11e1-b85c-61cd3cbb3210', 'Explicit options produce expected id');
+
+// Verify adjacent ids across a msec boundary are 1 time unit apart
+var u0 = uuid.v1({msecs: TIME, nsecs: 9999});
+var u1 = uuid.v1({msecs: TIME + 1, nsecs: 0});
+
+var before = u0.split('-')[0], after = u1.split('-')[0];
+var dt = parseInt(after, 16) - parseInt(before, 16);
+assert(dt === 1, 'Ids spanning 1ms boundary are 100ns apart');
+
+//
+// Test parse/unparse
+//
+
+id = '00112233445566778899aabbccddeeff';
+assert(uuid.unparse(uuid.parse(id.substr(0,10))) ==
+ '00112233-4400-0000-0000-000000000000', 'Short parse');
+assert(uuid.unparse(uuid.parse('(this is the uuid -> ' + id + id)) ==
+ '00112233-4455-6677-8899-aabbccddeeff', 'Dirty parse');
+
+//
+// Perf tests
+//
+
+var generators = {
+ v1: uuid.v1,
+ v4: uuid.v4
+};
+
+var UUID_FORMAT = {
+ v1: /[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i,
+ v4: /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i
+};
+
+var N = 1e4;
+
+// Get %'age an actual value differs from the ideal value
+function divergence(actual, ideal) {
+ return Math.round(100*100*(actual - ideal)/ideal)/100;
+}
+
+function rate(msg, t) {
+ log(msg + ': ' + (N / (Date.now() - t) * 1e3 | 0) + ' uuids\/second');
+}
+
+for (var version in generators) {
+ var counts = {}, max = 0;
+ var generator = generators[version];
+ var format = UUID_FORMAT[version];
+
+ log('\nSanity check ' + N + ' ' + version + ' uuids');
+ for (var i = 0, ok = 0; i < N; i++) {
+ id = generator();
+ if (!format.test(id)) {
+ throw Error(id + ' is not a valid UUID string');
+ }
+
+ if (id != uuid.unparse(uuid.parse(id))) {
+ assert(fail, id + ' is not a valid id');
+ }
+
+ // Count digits for our randomness check
+ if (version == 'v4') {
+ var digits = id.replace(/-/g, '').split('');
+ for (var j = digits.length-1; j >= 0; j--) {
+ var c = digits[j];
+ max = Math.max(max, counts[c] = (counts[c] || 0) + 1);
+ }
+ }
+ }
+
+ // Check randomness for v4 UUIDs
+ if (version == 'v4') {
+ // Limit that we get worried about randomness. (Purely empirical choice, this!)
+ var limit = 2*100*Math.sqrt(1/N);
+
+ log('\nChecking v4 randomness. Distribution of Hex Digits (% deviation from ideal)');
+
+ for (var i = 0; i < 16; i++) {
+ var c = i.toString(16);
+ var bar = '', n = counts[c], p = Math.round(n/max*100|0);
+
+ // 1-3,5-8, and D-F: 1:16 odds over 30 digits
+ var ideal = N*30/16;
+ if (i == 4) {
+ // 4: 1:1 odds on 1 digit, plus 1:16 odds on 30 digits
+ ideal = N*(1 + 30/16);
+ } else if (i >= 8 && i <= 11) {
+ // 8-B: 1:4 odds on 1 digit, plus 1:16 odds on 30 digits
+ ideal = N*(1/4 + 30/16);
+ } else {
+ // Otherwise: 1:16 odds on 30 digits
+ ideal = N*30/16;
+ }
+ var d = divergence(n, ideal);
+
+ // Draw bar using UTF squares (just for grins)
+ var s = n/max*50 | 0;
+ while (s--) bar += '=';
+
+ assert(Math.abs(d) < limit, c + ' |' + bar + '| ' + counts[c] + ' (' + d + '% < ' + limit + '%)');
+ }
+ }
+}
+
+// Perf tests
+for (var version in generators) {
+ log('\nPerformance testing ' + version + ' UUIDs');
+ var generator = generators[version];
+ var buf = new uuid.BufferClass(16);
+
+ for (var i = 0, t = Date.now(); i < N; i++) generator();
+ rate('uuid.' + version + '()', t);
+
+ for (var i = 0, t = Date.now(); i < N; i++) generator('binary');
+ rate('uuid.' + version + '(\'binary\')', t);
+
+ for (var i = 0, t = Date.now(); i < N; i++) generator('binary', buf);
+ rate('uuid.' + version + '(\'binary\', buffer)', t);
+}
--- /dev/null
+// uuid.js
+//
+// Copyright (c) 2010-2012 Robert Kieffer
+// MIT License - http://opensource.org/licenses/mit-license.php
+
+(function() {
+ var _global = this;
+
+ // Unique ID creation requires a high quality random # generator. We feature
+ // detect to determine the best RNG source, normalizing to a function that
+ // returns 128-bits of randomness, since that's what's usually required
+ var _rng;
+
+ // Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
+ //
+ // Moderately fast, high quality
+ if (typeof(_global.require) == 'function') {
+ try {
+ var _rb = _global.require('crypto').randomBytes;
+ _rng = _rb && function() {return _rb(16);};
+ } catch(e) {}
+ }
+
+ if (!_rng && _global.crypto && crypto.getRandomValues) {
+ // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
+ //
+ // Moderately fast, high quality
+ var _rnds8 = new Uint8Array(16);
+ _rng = function whatwgRNG() {
+ crypto.getRandomValues(_rnds8);
+ return _rnds8;
+ };
+ }
+
+ if (!_rng) {
+ // Math.random()-based (RNG)
+ //
+ // If all else fails, use Math.random(). It's fast, but is of unspecified
+ // quality.
+ var _rnds = new Array(16);
+ _rng = function() {
+ for (var i = 0, r; i < 16; i++) {
+ if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
+ _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
+ }
+
+ return _rnds;
+ };
+ }
+
+ // Buffer class to use
+ var BufferClass = typeof(_global.Buffer) == 'function' ? _global.Buffer : Array;
+
+ // Maps for number <-> hex string conversion
+ var _byteToHex = [];
+ var _hexToByte = {};
+ for (var i = 0; i < 256; i++) {
+ _byteToHex[i] = (i + 0x100).toString(16).substr(1);
+ _hexToByte[_byteToHex[i]] = i;
+ }
+
+ // **`parse()` - Parse a UUID into it's component bytes**
+ function parse(s, buf, offset) {
+ var i = (buf && offset) || 0, ii = 0;
+
+ buf = buf || [];
+ s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
+ if (ii < 16) { // Don't overflow!
+ buf[i + ii++] = _hexToByte[oct];
+ }
+ });
+
+ // Zero out remaining bytes if string was short
+ while (ii < 16) {
+ buf[i + ii++] = 0;
+ }
+
+ return buf;
+ }
+
+ // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
+ function unparse(buf, offset) {
+ var i = offset || 0, bth = _byteToHex;
+ return bth[buf[i++]] + bth[buf[i++]] +
+ bth[buf[i++]] + bth[buf[i++]] + '-' +
+ bth[buf[i++]] + bth[buf[i++]] + '-' +
+ bth[buf[i++]] + bth[buf[i++]] + '-' +
+ bth[buf[i++]] + bth[buf[i++]] + '-' +
+ bth[buf[i++]] + bth[buf[i++]] +
+ bth[buf[i++]] + bth[buf[i++]] +
+ bth[buf[i++]] + bth[buf[i++]];
+ }
+
+ // **`v1()` - Generate time-based UUID**
+ //
+ // Inspired by https://github.com/LiosK/UUID.js
+ // and http://docs.python.org/library/uuid.html
+
+ // random #'s we need to init node and clockseq
+ var _seedBytes = _rng();
+
+ // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
+ var _nodeId = [
+ _seedBytes[0] | 0x01,
+ _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
+ ];
+
+ // Per 4.2.2, randomize (14 bit) clockseq
+ var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
+
+ // Previous uuid creation time
+ var _lastMSecs = 0, _lastNSecs = 0;
+
+ // See https://github.com/broofa/node-uuid for API details
+ function v1(options, buf, offset) {
+ var i = buf && offset || 0;
+ var b = buf || [];
+
+ options = options || {};
+
+ var clockseq = options.clockseq != null ? options.clockseq : _clockseq;
+
+ // UUID timestamps are 100 nano-second units since the Gregorian epoch,
+ // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
+ // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
+ // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
+ var msecs = options.msecs != null ? options.msecs : new Date().getTime();
+
+ // Per 4.2.1.2, use count of uuid's generated during the current clock
+ // cycle to simulate higher resolution clock
+ var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1;
+
+ // Time since last uuid creation (in msecs)
+ var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
+
+ // Per 4.2.1.2, Bump clockseq on clock regression
+ if (dt < 0 && options.clockseq == null) {
+ clockseq = clockseq + 1 & 0x3fff;
+ }
+
+ // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
+ // time interval
+ if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
+ nsecs = 0;
+ }
+
+ // Per 4.2.1.2 Throw error if too many uuids are requested
+ if (nsecs >= 10000) {
+ throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
+ }
+
+ _lastMSecs = msecs;
+ _lastNSecs = nsecs;
+ _clockseq = clockseq;
+
+ // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
+ msecs += 12219292800000;
+
+ // `time_low`
+ var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
+ b[i++] = tl >>> 24 & 0xff;
+ b[i++] = tl >>> 16 & 0xff;
+ b[i++] = tl >>> 8 & 0xff;
+ b[i++] = tl & 0xff;
+
+ // `time_mid`
+ var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
+ b[i++] = tmh >>> 8 & 0xff;
+ b[i++] = tmh & 0xff;
+
+ // `time_high_and_version`
+ b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
+ b[i++] = tmh >>> 16 & 0xff;
+
+ // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
+ b[i++] = clockseq >>> 8 | 0x80;
+
+ // `clock_seq_low`
+ b[i++] = clockseq & 0xff;
+
+ // `node`
+ var node = options.node || _nodeId;
+ for (var n = 0; n < 6; n++) {
+ b[i + n] = node[n];
+ }
+
+ return buf ? buf : unparse(b);
+ }
+
+ // **`v4()` - Generate random UUID**
+
+ // See https://github.com/broofa/node-uuid for API details
+ function v4(options, buf, offset) {
+ // Deprecated - 'format' argument, as supported in v1.2
+ var i = buf && offset || 0;
+
+ if (typeof(options) == 'string') {
+ buf = options == 'binary' ? new BufferClass(16) : null;
+ options = null;
+ }
+ options = options || {};
+
+ var rnds = options.random || (options.rng || _rng)();
+
+ // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
+ rnds[6] = (rnds[6] & 0x0f) | 0x40;
+ rnds[8] = (rnds[8] & 0x3f) | 0x80;
+
+ // Copy bytes to buffer, if provided
+ if (buf) {
+ for (var ii = 0; ii < 16; ii++) {
+ buf[i + ii] = rnds[ii];
+ }
+ }
+
+ return buf || unparse(rnds);
+ }
+
+ // Export public API
+ var uuid = v4;
+ uuid.v1 = v1;
+ uuid.v4 = v4;
+ uuid.parse = parse;
+ uuid.unparse = unparse;
+ uuid.BufferClass = BufferClass;
+
+ if (typeof(module) != 'undefined' && module.exports) {
+ // Publish as node.js module
+ module.exports = uuid;
+ } else if (typeof define === 'function' && define.amd) {
+ // Publish as AMD module
+ define(function() {return uuid;});
+
+
+ } else {
+ // Publish as global (in browsers)
+ var _previousRoot = _global.uuid;
+
+ // **`noConflict()` - (browser only) to reset global 'uuid' var**
+ uuid.noConflict = function() {
+ _global.uuid = _previousRoot;
+ return uuid;
+ };
+
+ _global.uuid = uuid;
+ }
+}).call(this);
--- /dev/null
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
--- /dev/null
+oauth-sign
+==========
+
+OAuth 1 signing. Formerly a vendor lib in mikeal/request, now a standalone module.
--- /dev/null
+var crypto = require('crypto')
+ , qs = require('querystring')
+ ;
+
+function sha1 (key, body) {
+ return crypto.createHmac('sha1', key).update(body).digest('base64')
+}
+
+function rsa (key, body) {
+ return crypto.createSign("RSA-SHA1").update(body).sign(key, 'base64');
+}
+
+function rfc3986 (str) {
+ return encodeURIComponent(str)
+ .replace(/!/g,'%21')
+ .replace(/\*/g,'%2A')
+ .replace(/\(/g,'%28')
+ .replace(/\)/g,'%29')
+ .replace(/'/g,'%27')
+ ;
+}
+
+// Maps object to bi-dimensional array
+// Converts { foo: 'A', bar: [ 'b', 'B' ]} to
+// [ ['foo', 'A'], ['bar', 'b'], ['bar', 'B'] ]
+function map (obj) {
+ var key, val, arr = []
+ for (key in obj) {
+ val = obj[key]
+ if (Array.isArray(val))
+ for (var i = 0; i < val.length; i++)
+ arr.push([key, val[i]])
+ else
+ arr.push([key, val])
+ }
+ return arr
+}
+
+// Compare function for sort
+function compare (a, b) {
+ return a > b ? 1 : a < b ? -1 : 0
+}
+
+function generateBase (httpMethod, base_uri, params) {
+ // adapted from https://dev.twitter.com/docs/auth/oauth and
+ // https://dev.twitter.com/docs/auth/creating-signature
+
+ // Parameter normalization
+ // http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
+ var normalized = map(params)
+ // 1. First, the name and value of each parameter are encoded
+ .map(function (p) {
+ return [ rfc3986(p[0]), rfc3986(p[1] || '') ]
+ })
+ // 2. The parameters are sorted by name, using ascending byte value
+ // ordering. If two or more parameters share the same name, they
+ // are sorted by their value.
+ .sort(function (a, b) {
+ return compare(a[0], b[0]) || compare(a[1], b[1])
+ })
+ // 3. The name of each parameter is concatenated to its corresponding
+ // value using an "=" character (ASCII code 61) as a separator, even
+ // if the value is empty.
+ .map(function (p) { return p.join('=') })
+ // 4. The sorted name/value pairs are concatenated together into a
+ // single string by using an "&" character (ASCII code 38) as
+ // separator.
+ .join('&')
+
+ var base = [
+ rfc3986(httpMethod ? httpMethod.toUpperCase() : 'GET'),
+ rfc3986(base_uri),
+ rfc3986(normalized)
+ ].join('&')
+
+ return base
+}
+
+function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret) {
+ var base = generateBase(httpMethod, base_uri, params)
+ var key = [
+ consumer_secret || '',
+ token_secret || ''
+ ].map(rfc3986).join('&')
+
+ return sha1(key, base)
+}
+
+function rsasign (httpMethod, base_uri, params, private_key, token_secret) {
+ var base = generateBase(httpMethod, base_uri, params)
+ var key = private_key || ''
+
+ return rsa(key, base)
+}
+
+function plaintext (consumer_secret, token_secret) {
+ var key = [
+ consumer_secret || '',
+ token_secret || ''
+ ].map(rfc3986).join('&')
+
+ return key
+}
+
+function sign (signMethod, httpMethod, base_uri, params, consumer_secret, token_secret) {
+ var method
+ var skipArgs = 1
+
+ switch (signMethod) {
+ case 'RSA-SHA1':
+ method = rsasign
+ break
+ case 'HMAC-SHA1':
+ method = hmacsign
+ break
+ case 'PLAINTEXT':
+ method = plaintext
+ skipArgs = 4
+ break
+ default:
+ throw new Error("Signature method not supported: " + signMethod)
+ }
+
+ return method.apply(null, [].slice.call(arguments, skipArgs))
+}
+
+exports.hmacsign = hmacsign
+exports.rsasign = rsasign
+exports.plaintext = plaintext
+exports.sign = sign
+exports.rfc3986 = rfc3986
--- /dev/null
+{
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com",
+ "url": "http://www.futurealoof.com"
+ },
+ "name": "oauth-sign",
+ "description": "OAuth 1 signing. Formerly a vendor lib in mikeal/request, now a standalone module.",
+ "version": "0.6.0",
+ "repository": {
+ "url": "git+https://github.com/mikeal/oauth-sign.git"
+ },
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "scripts": {
+ "test": "node test.js"
+ },
+ "gitHead": "f1b5d7714712ab7eec485cca9d18ae95db58aa6b",
+ "bugs": {
+ "url": "https://github.com/mikeal/oauth-sign/issues"
+ },
+ "homepage": "https://github.com/mikeal/oauth-sign",
+ "_id": "oauth-sign@0.6.0",
+ "_shasum": "7dbeae44f6ca454e1f168451d630746735813ce3",
+ "_from": "oauth-sign@>=0.6.0 <0.7.0",
+ "_npmVersion": "1.4.14",
+ "_npmUser": {
+ "name": "nylen",
+ "email": "jnylen@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ {
+ "name": "nylen",
+ "email": "jnylen@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "7dbeae44f6ca454e1f168451d630746735813ce3",
+ "tarball": "http://registry.npmjs.org/oauth-sign/-/oauth-sign-0.6.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.6.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
--- /dev/null
+var oauth = require('./index')
+ , hmacsign = oauth.hmacsign
+ , assert = require('assert')
+ , qs = require('querystring')
+ ;
+
+// Tests from Twitter documentation https://dev.twitter.com/docs/auth/oauth
+
+var reqsign = hmacsign('POST', 'https://api.twitter.com/oauth/request_token',
+ { oauth_callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11'
+ , oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g'
+ , oauth_nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk'
+ , oauth_signature_method: 'HMAC-SHA1'
+ , oauth_timestamp: '1272323042'
+ , oauth_version: '1.0'
+ }, "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98")
+
+console.log(reqsign)
+console.log('8wUi7m5HFQy76nowoCThusfgB+Q=')
+assert.equal(reqsign, '8wUi7m5HFQy76nowoCThusfgB+Q=')
+
+var accsign = hmacsign('POST', 'https://api.twitter.com/oauth/access_token',
+ { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g'
+ , oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8'
+ , oauth_signature_method: 'HMAC-SHA1'
+ , oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc'
+ , oauth_timestamp: '1272323047'
+ , oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY'
+ , oauth_version: '1.0'
+ }, "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98", "x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA")
+
+console.log(accsign)
+console.log('PUw/dHA4fnlJYM6RhXk5IU/0fCc=')
+assert.equal(accsign, 'PUw/dHA4fnlJYM6RhXk5IU/0fCc=')
+
+var upsign = hmacsign('POST', 'http://api.twitter.com/1/statuses/update.json',
+ { oauth_consumer_key: "GDdmIQH6jhtmLUypg82g"
+ , oauth_nonce: "oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y"
+ , oauth_signature_method: "HMAC-SHA1"
+ , oauth_token: "819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw"
+ , oauth_timestamp: "1272325550"
+ , oauth_version: "1.0"
+ , status: 'setting up my twitter 私のさえずりを設定する'
+ }, "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98", "J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA")
+
+console.log(upsign)
+console.log('yOahq5m0YjDDjfjxHaXEsW9D+X0=')
+assert.equal(upsign, 'yOahq5m0YjDDjfjxHaXEsW9D+X0=')
+
+// example in rfc5849
+var params = qs.parse('b5=%3D%253D&a3=a&c%40=&a2=r%20b' + '&' + 'c2&a3=2+q')
+params.oauth_consumer_key = '9djdj82h48djs9d2'
+params.oauth_token = 'kkk9d7dh3k39sjv7'
+params.oauth_nonce = '7d8f3e4a'
+params.oauth_signature_method = 'HMAC-SHA1'
+params.oauth_timestamp = '137131201'
+
+var rfc5849sign = hmacsign('POST', 'http://example.com/request',
+ params, "j49sk3j29djd", "dh893hdasih9")
+
+console.log(rfc5849sign)
+console.log('r6/TJjbCOr97/+UU0NsvSne7s5g=')
+assert.equal(rfc5849sign, 'r6/TJjbCOr97/+UU0NsvSne7s5g=')
+
+
+// PLAINTEXT
+
+var plainSign = oauth.sign('PLAINTEXT', 'GET', 'http://dummy.com', {}, 'consumer_secret', 'token_secret')
+console.log(plainSign)
+assert.equal(plainSign, 'consumer_secret&token_secret')
+
+plainSign = oauth.plaintext('consumer_secret', 'token_secret')
+console.log(plainSign)
+assert.equal(plainSign, 'consumer_secret&token_secret')
--- /dev/null
+node_modules
--- /dev/null
+{
+ "node": true,
+
+ "curly": true,
+ "latedef": true,
+ "quotmark": true,
+ "undef": true,
+ "unused": true,
+ "trailing": true
+}
--- /dev/null
+.idea
+*.iml
+npm-debug.log
+dump.rdb
+node_modules
+results.tap
+results.xml
+npm-shrinkwrap.json
+config.json
+.DS_Store
+*/.DS_Store
+*/*/.DS_Store
+._*
+*/._*
+*/*/._*
+coverage.*
+lib-cov
+complexity.md
--- /dev/null
+language: node_js
+
+node_js:
+ - 0.10
+ - 0.12
+ - iojs
--- /dev/null
+
+## [**2.4.1**](https://github.com/hapijs/qs/issues?milestone=20&state=closed)
+- [**#73**](https://github.com/hapijs/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
+
+## [**2.4.0**](https://github.com/hapijs/qs/issues?milestone=19&state=closed)
+- [**#70**](https://github.com/hapijs/qs/issues/70) Add arrayFormat option
+
+## [**2.3.3**](https://github.com/hapijs/qs/issues?milestone=18&state=closed)
+- [**#59**](https://github.com/hapijs/qs/issues/59) make sure array indexes are >= 0, closes #57
+- [**#58**](https://github.com/hapijs/qs/issues/58) make qs usable for browser loader
+
+## [**2.3.2**](https://github.com/hapijs/qs/issues?milestone=17&state=closed)
+- [**#55**](https://github.com/hapijs/qs/issues/55) allow merging a string into an object
+
+## [**2.3.1**](https://github.com/hapijs/qs/issues?milestone=16&state=closed)
+- [**#52**](https://github.com/hapijs/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
+
+## [**2.3.0**](https://github.com/hapijs/qs/issues?milestone=15&state=closed)
+- [**#50**](https://github.com/hapijs/qs/issues/50) add option to omit array indices, closes #46
+
+## [**2.2.5**](https://github.com/hapijs/qs/issues?milestone=14&state=closed)
+- [**#39**](https://github.com/hapijs/qs/issues/39) Is there an alternative to Buffer.isBuffer?
+- [**#49**](https://github.com/hapijs/qs/issues/49) refactor utils.merge, fixes #45
+- [**#41**](https://github.com/hapijs/qs/issues/41) avoid browserifying Buffer, for #39
+
+## [**2.2.4**](https://github.com/hapijs/qs/issues?milestone=13&state=closed)
+- [**#38**](https://github.com/hapijs/qs/issues/38) how to handle object keys beginning with a number
+
+## [**2.2.3**](https://github.com/hapijs/qs/issues?milestone=12&state=closed)
+- [**#37**](https://github.com/hapijs/qs/issues/37) parser discards first empty value in array
+- [**#36**](https://github.com/hapijs/qs/issues/36) Update to lab 4.x
+
+## [**2.2.2**](https://github.com/hapijs/qs/issues?milestone=11&state=closed)
+- [**#33**](https://github.com/hapijs/qs/issues/33) Error when plain object in a value
+- [**#34**](https://github.com/hapijs/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
+- [**#24**](https://github.com/hapijs/qs/issues/24) Changelog? Semver?
+
+## [**2.2.1**](https://github.com/hapijs/qs/issues?milestone=10&state=closed)
+- [**#32**](https://github.com/hapijs/qs/issues/32) account for circular references properly, closes #31
+- [**#31**](https://github.com/hapijs/qs/issues/31) qs.parse stackoverflow on circular objects
+
+## [**2.2.0**](https://github.com/hapijs/qs/issues?milestone=9&state=closed)
+- [**#26**](https://github.com/hapijs/qs/issues/26) Don't use Buffer global if it's not present
+- [**#30**](https://github.com/hapijs/qs/issues/30) Bug when merging non-object values into arrays
+- [**#29**](https://github.com/hapijs/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
+- [**#23**](https://github.com/hapijs/qs/issues/23) Ability to not limit parameters?
+
+## [**2.1.0**](https://github.com/hapijs/qs/issues?milestone=8&state=closed)
+- [**#22**](https://github.com/hapijs/qs/issues/22) Enable using a RegExp as delimiter
+
+## [**2.0.0**](https://github.com/hapijs/qs/issues?milestone=7&state=closed)
+- [**#18**](https://github.com/hapijs/qs/issues/18) Why is there arrayLimit?
+- [**#20**](https://github.com/hapijs/qs/issues/20) Configurable parametersLimit
+- [**#21**](https://github.com/hapijs/qs/issues/21) make all limits optional, for #18, for #20
+
+## [**1.2.2**](https://github.com/hapijs/qs/issues?milestone=6&state=closed)
+- [**#19**](https://github.com/hapijs/qs/issues/19) Don't overwrite null values
+
+## [**1.2.1**](https://github.com/hapijs/qs/issues?milestone=5&state=closed)
+- [**#16**](https://github.com/hapijs/qs/issues/16) ignore non-string delimiters
+- [**#15**](https://github.com/hapijs/qs/issues/15) Close code block
+
+## [**1.2.0**](https://github.com/hapijs/qs/issues?milestone=4&state=closed)
+- [**#12**](https://github.com/hapijs/qs/issues/12) Add optional delim argument
+- [**#13**](https://github.com/hapijs/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
+
+## [**1.1.0**](https://github.com/hapijs/qs/issues?milestone=3&state=closed)
+- [**#7**](https://github.com/hapijs/qs/issues/7) Empty values of a POST array disappear after being submitted
+- [**#9**](https://github.com/hapijs/qs/issues/9) Should not omit equals signs (=) when value is null
+- [**#6**](https://github.com/hapijs/qs/issues/6) Minor grammar fix in README
+
+## [**1.0.2**](https://github.com/hapijs/qs/issues?milestone=2&state=closed)
+- [**#5**](https://github.com/hapijs/qs/issues/5) array holes incorrectly copied into object on large index
--- /dev/null
+Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md).
--- /dev/null
+Copyright (c) 2014 Nathan LaFreniere and other contributors.
+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.
+ * The names of any contributors may not 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS 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.
+
+ * * *
+
+The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors
--- /dev/null
+test:
+ @node node_modules/lab/bin/lab -a code -L
+test-cov:
+ @node node_modules/lab/bin/lab -a code -t 100 -L
+test-cov-html:
+ @node node_modules/lab/bin/lab -a code -L -r html -o coverage.html
+
+.PHONY: test test-cov test-cov-html
--- /dev/null
+# qs
+
+A querystring parsing and stringifying library with some added security.
+
+[](http://travis-ci.org/hapijs/qs)
+
+Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf)
+
+The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
+
+## Usage
+
+```javascript
+var Qs = require('qs');
+
+var obj = Qs.parse('a=c'); // { a: 'c' }
+var str = Qs.stringify(obj); // 'a=c'
+```
+
+### Parsing Objects
+
+```javascript
+Qs.parse(string, [options]);
+```
+
+**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
+For example, the string `'foo[bar]=baz'` converts to:
+
+```javascript
+{
+ foo: {
+ bar: 'baz'
+ }
+}
+```
+
+URI encoded strings work too:
+
+```javascript
+Qs.parse('a%5Bb%5D=c');
+// { a: { b: 'c' } }
+```
+
+You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
+
+```javascript
+{
+ foo: {
+ bar: {
+ baz: 'foobarbaz'
+ }
+ }
+}
+```
+
+By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like
+`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
+
+```javascript
+{
+ a: {
+ b: {
+ c: {
+ d: {
+ e: {
+ f: {
+ '[g][h][i]': 'j'
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+This depth can be overridden by passing a `depth` option to `Qs.parse(string, [options])`:
+
+```javascript
+Qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
+// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } }
+```
+
+The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.
+
+For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:
+
+```javascript
+Qs.parse('a=b&c=d', { parameterLimit: 1 });
+// { a: 'b' }
+```
+
+An optional delimiter can also be passed:
+
+```javascript
+Qs.parse('a=b;c=d', { delimiter: ';' });
+// { a: 'b', c: 'd' }
+```
+
+Delimiters can be a regular expression too:
+
+```javascript
+Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
+// { a: 'b', c: 'd', e: 'f' }
+```
+
+### Parsing Arrays
+
+**qs** can also parse arrays using a similar `[]` notation:
+
+```javascript
+Qs.parse('a[]=b&a[]=c');
+// { a: ['b', 'c'] }
+```
+
+You may specify an index as well:
+
+```javascript
+Qs.parse('a[1]=c&a[0]=b');
+// { a: ['b', 'c'] }
+```
+
+Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number
+to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving
+their order:
+
+```javascript
+Qs.parse('a[1]=b&a[15]=c');
+// { a: ['b', 'c'] }
+```
+
+Note that an empty string is also a value, and will be preserved:
+
+```javascript
+Qs.parse('a[]=&a[]=b');
+// { a: ['', 'b'] }
+Qs.parse('a[0]=b&a[1]=&a[2]=c');
+// { a: ['b', '', 'c'] }
+```
+
+**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
+instead be converted to an object with the index as the key:
+
+```javascript
+Qs.parse('a[100]=b');
+// { a: { '100': 'b' } }
+```
+
+This limit can be overridden by passing an `arrayLimit` option:
+
+```javascript
+Qs.parse('a[1]=b', { arrayLimit: 0 });
+// { a: { '1': 'b' } }
+```
+
+To disable array parsing entirely, set `arrayLimit` to `-1`.
+
+If you mix notations, **qs** will merge the two items into an object:
+
+```javascript
+Qs.parse('a[0]=b&a[b]=c');
+// { a: { '0': 'b', b: 'c' } }
+```
+
+You can also create arrays of objects:
+
+```javascript
+Qs.parse('a[][b]=c');
+// { a: [{ b: 'c' }] }
+```
+
+### Stringifying
+
+```javascript
+Qs.stringify(object, [options]);
+```
+
+When stringifying, **qs** always URI encodes output. Objects are stringified as you would expect:
+
+```javascript
+Qs.stringify({ a: 'b' });
+// 'a=b'
+Qs.stringify({ a: { b: 'c' } });
+// 'a%5Bb%5D=c'
+```
+
+Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
+
+When arrays are stringified, by default they are given explicit indices:
+
+```javascript
+Qs.stringify({ a: ['b', 'c', 'd'] });
+// 'a[0]=b&a[1]=c&a[2]=d'
+```
+
+You may override this by setting the `indices` option to `false`:
+
+```javascript
+Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
+// 'a=b&a=c&a=d'
+```
+
+You may use the `arrayFormat` option to specify the format of the output array
+
+```javascript
+Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
+// 'a[0]=b&a[1]=c'
+Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
+// 'a[]=b&a[]=c'
+Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
+// 'a=b&a=c'
+```
+
+Empty strings and null values will omit the value, but the equals sign (=) remains in place:
+
+```javascript
+Qs.stringify({ a: '' });
+// 'a='
+```
+
+Properties that are set to `undefined` will be omitted entirely:
+
+```javascript
+Qs.stringify({ a: null, b: undefined });
+// 'a='
+```
+
+The delimiter may be overridden with stringify as well:
+
+```javascript
+Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' });
+// 'a=b;c=d'
+```
--- /dev/null
+module.exports = require('./lib/');
--- /dev/null
+// Load modules
+
+var Stringify = require('./stringify');
+var Parse = require('./parse');
+
+
+// Declare internals
+
+var internals = {};
+
+
+module.exports = {
+ stringify: Stringify,
+ parse: Parse
+};
--- /dev/null
+// Load modules
+
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {
+ delimiter: '&',
+ depth: 5,
+ arrayLimit: 20,
+ parameterLimit: 1000
+};
+
+
+internals.parseValues = function (str, options) {
+
+ var obj = {};
+ var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
+
+ for (var i = 0, il = parts.length; i < il; ++i) {
+ var part = parts[i];
+ var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
+
+ if (pos === -1) {
+ obj[Utils.decode(part)] = '';
+ }
+ else {
+ var key = Utils.decode(part.slice(0, pos));
+ var val = Utils.decode(part.slice(pos + 1));
+
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
+ obj[key] = val;
+ }
+ else {
+ obj[key] = [].concat(obj[key]).concat(val);
+ }
+ }
+ }
+
+ return obj;
+};
+
+
+internals.parseObject = function (chain, val, options) {
+
+ if (!chain.length) {
+ return val;
+ }
+
+ var root = chain.shift();
+
+ var obj = {};
+ if (root === '[]') {
+ obj = [];
+ obj = obj.concat(internals.parseObject(chain, val, options));
+ }
+ else {
+ var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
+ var index = parseInt(cleanRoot, 10);
+ var indexString = '' + index;
+ if (!isNaN(index) &&
+ root !== cleanRoot &&
+ indexString === cleanRoot &&
+ index >= 0 &&
+ index <= options.arrayLimit) {
+
+ obj = [];
+ obj[index] = internals.parseObject(chain, val, options);
+ }
+ else {
+ obj[cleanRoot] = internals.parseObject(chain, val, options);
+ }
+ }
+
+ return obj;
+};
+
+
+internals.parseKeys = function (key, val, options) {
+
+ if (!key) {
+ return;
+ }
+
+ // The regex chunks
+
+ var parent = /^([^\[\]]*)/;
+ var child = /(\[[^\[\]]*\])/g;
+
+ // Get the parent
+
+ var segment = parent.exec(key);
+
+ // Don't allow them to overwrite object prototype properties
+
+ if (Object.prototype.hasOwnProperty(segment[1])) {
+ return;
+ }
+
+ // Stash the parent if it exists
+
+ var keys = [];
+ if (segment[1]) {
+ keys.push(segment[1]);
+ }
+
+ // Loop through children appending to the array until we hit depth
+
+ var i = 0;
+ while ((segment = child.exec(key)) !== null && i < options.depth) {
+
+ ++i;
+ if (!Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) {
+ keys.push(segment[1]);
+ }
+ }
+
+ // If there's a remainder, just add whatever is left
+
+ if (segment) {
+ keys.push('[' + key.slice(segment.index) + ']');
+ }
+
+ return internals.parseObject(keys, val, options);
+};
+
+
+module.exports = function (str, options) {
+
+ if (str === '' ||
+ str === null ||
+ typeof str === 'undefined') {
+
+ return {};
+ }
+
+ options = options || {};
+ options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
+ options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
+ options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
+ options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
+
+ var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
+ var obj = {};
+
+ // Iterate over the keys and setup the new object
+
+ var keys = Object.keys(tempObj);
+ for (var i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ var newObj = internals.parseKeys(key, tempObj[key], options);
+ obj = Utils.merge(obj, newObj);
+ }
+
+ return Utils.compact(obj);
+};
--- /dev/null
+// Load modules
+
+var Utils = require('./utils');
+
+
+// Declare internals
+
+var internals = {
+ delimiter: '&',
+ arrayPrefixGenerators: {
+ brackets: function (prefix, key) {
+ return prefix + '[]';
+ },
+ indices: function (prefix, key) {
+ return prefix + '[' + key + ']';
+ },
+ repeat: function (prefix, key) {
+ return prefix;
+ }
+ }
+};
+
+
+internals.stringify = function (obj, prefix, generateArrayPrefix) {
+
+ if (Utils.isBuffer(obj)) {
+ obj = obj.toString();
+ }
+ else if (obj instanceof Date) {
+ obj = obj.toISOString();
+ }
+ else if (obj === null) {
+ obj = '';
+ }
+
+ if (typeof obj === 'string' ||
+ typeof obj === 'number' ||
+ typeof obj === 'boolean') {
+
+ return [encodeURIComponent(prefix) + '=' + encodeURIComponent(obj)];
+ }
+
+ var values = [];
+
+ if (typeof obj === 'undefined') {
+ return values;
+ }
+
+ var objKeys = Object.keys(obj);
+ for (var i = 0, il = objKeys.length; i < il; ++i) {
+ var key = objKeys[i];
+ if (Array.isArray(obj)) {
+ values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix));
+ }
+ else {
+ values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix));
+ }
+ }
+
+ return values;
+};
+
+
+module.exports = function (obj, options) {
+
+ options = options || {};
+ var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
+
+ var keys = [];
+
+ if (typeof obj !== 'object' ||
+ obj === null) {
+
+ return '';
+ }
+
+ var arrayFormat;
+ if (options.arrayFormat in internals.arrayPrefixGenerators) {
+ arrayFormat = options.arrayFormat;
+ }
+ else if ('indices' in options) {
+ arrayFormat = options.indices ? 'indices' : 'repeat';
+ }
+ else {
+ arrayFormat = 'indices';
+ }
+
+ var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
+
+ var objKeys = Object.keys(obj);
+ for (var i = 0, il = objKeys.length; i < il; ++i) {
+ var key = objKeys[i];
+ keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix));
+ }
+
+ return keys.join(delimiter);
+};
--- /dev/null
+// Load modules
+
+
+// Declare internals
+
+var internals = {};
+
+
+exports.arrayToObject = function (source) {
+
+ var obj = {};
+ for (var i = 0, il = source.length; i < il; ++i) {
+ if (typeof source[i] !== 'undefined') {
+
+ obj[i] = source[i];
+ }
+ }
+
+ return obj;
+};
+
+
+exports.merge = function (target, source) {
+
+ if (!source) {
+ return target;
+ }
+
+ if (typeof source !== 'object') {
+ if (Array.isArray(target)) {
+ target.push(source);
+ }
+ else {
+ target[source] = true;
+ }
+
+ return target;
+ }
+
+ if (typeof target !== 'object') {
+ target = [target].concat(source);
+ return target;
+ }
+
+ if (Array.isArray(target) &&
+ !Array.isArray(source)) {
+
+ target = exports.arrayToObject(target);
+ }
+
+ var keys = Object.keys(source);
+ for (var k = 0, kl = keys.length; k < kl; ++k) {
+ var key = keys[k];
+ var value = source[key];
+
+ if (!target[key]) {
+ target[key] = value;
+ }
+ else {
+ target[key] = exports.merge(target[key], value);
+ }
+ }
+
+ return target;
+};
+
+
+exports.decode = function (str) {
+
+ try {
+ return decodeURIComponent(str.replace(/\+/g, ' '));
+ } catch (e) {
+ return str;
+ }
+};
+
+
+exports.compact = function (obj, refs) {
+
+ if (typeof obj !== 'object' ||
+ obj === null) {
+
+ return obj;
+ }
+
+ refs = refs || [];
+ var lookup = refs.indexOf(obj);
+ if (lookup !== -1) {
+ return refs[lookup];
+ }
+
+ refs.push(obj);
+
+ if (Array.isArray(obj)) {
+ var compacted = [];
+
+ for (var i = 0, il = obj.length; i < il; ++i) {
+ if (typeof obj[i] !== 'undefined') {
+ compacted.push(obj[i]);
+ }
+ }
+
+ return compacted;
+ }
+
+ var keys = Object.keys(obj);
+ for (i = 0, il = keys.length; i < il; ++i) {
+ var key = keys[i];
+ obj[key] = exports.compact(obj[key], refs);
+ }
+
+ return obj;
+};
+
+
+exports.isRegExp = function (obj) {
+ return Object.prototype.toString.call(obj) === '[object RegExp]';
+};
+
+
+exports.isBuffer = function (obj) {
+
+ if (obj === null ||
+ typeof obj === 'undefined') {
+
+ return false;
+ }
+
+ return !!(obj.constructor &&
+ obj.constructor.isBuffer &&
+ obj.constructor.isBuffer(obj));
+};
--- /dev/null
+{
+ "name": "qs",
+ "version": "2.4.2",
+ "description": "A querystring parser that supports nesting and arrays, with a depth limit",
+ "homepage": "https://github.com/hapijs/qs",
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {
+ "code": "1.x.x",
+ "lab": "5.x.x"
+ },
+ "scripts": {
+ "test": "make test-cov"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/hapijs/qs.git"
+ },
+ "keywords": [
+ "querystring",
+ "qs"
+ ],
+ "licenses": [
+ {
+ "type": "BSD",
+ "url": "http://github.com/hapijs/qs/raw/master/LICENSE"
+ }
+ ],
+ "gitHead": "cdd64a9d1385dbc3dde48da6de98b5993f1607bd",
+ "bugs": {
+ "url": "https://github.com/hapijs/qs/issues"
+ },
+ "_id": "qs@2.4.2",
+ "_shasum": "f7ce788e5777df0b5010da7f7c4e73ba32470f5a",
+ "_from": "qs@>=2.4.0 <2.5.0",
+ "_npmVersion": "2.9.0",
+ "_nodeVersion": "0.10.38",
+ "_npmUser": {
+ "name": "nlf",
+ "email": "quitlahok@gmail.com"
+ },
+ "dist": {
+ "shasum": "f7ce788e5777df0b5010da7f7c4e73ba32470f5a",
+ "tarball": "http://registry.npmjs.org/qs/-/qs-2.4.2.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "nlf",
+ "email": "quitlahok@gmail.com"
+ },
+ {
+ "name": "hueniverse",
+ "email": "eran@hueniverse.com"
+ }
+ ],
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz"
+}
--- /dev/null
+/* eslint no-extend-native:0 */
+// Load modules
+
+var Code = require('code');
+var Lab = require('lab');
+var Qs = require('../');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var expect = Code.expect;
+var describe = lab.experiment;
+var it = lab.test;
+
+
+describe('parse()', function () {
+
+ it('parses a simple string', function (done) {
+
+ expect(Qs.parse('0=foo')).to.deep.equal({ '0': 'foo' });
+ expect(Qs.parse('foo=c++')).to.deep.equal({ foo: 'c ' });
+ expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } });
+ expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } });
+ expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } });
+ expect(Qs.parse('foo')).to.deep.equal({ foo: '' });
+ expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' });
+ expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' });
+ expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' });
+ expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' });
+ expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' });
+ expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({
+ cht: 'p3',
+ chd: 't:60,40',
+ chs: '250x100',
+ chl: 'Hello|World'
+ });
+ done();
+ });
+
+ it('parses a single nested string', function (done) {
+
+ expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } });
+ done();
+ });
+
+ it('parses a double nested string', function (done) {
+
+ expect(Qs.parse('a[b][c]=d')).to.deep.equal({ a: { b: { c: 'd' } } });
+ done();
+ });
+
+ it('defaults to a depth of 5', function (done) {
+
+ expect(Qs.parse('a[b][c][d][e][f][g][h]=i')).to.deep.equal({ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } });
+ done();
+ });
+
+ it('only parses one level when depth = 1', function (done) {
+
+ expect(Qs.parse('a[b][c]=d', { depth: 1 })).to.deep.equal({ a: { b: { '[c]': 'd' } } });
+ expect(Qs.parse('a[b][c][d]=e', { depth: 1 })).to.deep.equal({ a: { b: { '[c][d]': 'e' } } });
+ done();
+ });
+
+ it('parses a simple array', function (done) {
+
+ expect(Qs.parse('a=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+ done();
+ });
+
+ it('parses an explicit array', function (done) {
+
+ expect(Qs.parse('a[]=b')).to.deep.equal({ a: ['b'] });
+ expect(Qs.parse('a[]=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a[]=b&a[]=c&a[]=d')).to.deep.equal({ a: ['b', 'c', 'd'] });
+ done();
+ });
+
+ it('parses a mix of simple and explicit arrays', function (done) {
+
+ expect(Qs.parse('a=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a[]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a[0]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a=b&a[0]=c')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a[1]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a=b&a[1]=c')).to.deep.equal({ a: ['b', 'c'] });
+ done();
+ });
+
+ it('parses a nested array', function (done) {
+
+ expect(Qs.parse('a[b][]=c&a[b][]=d')).to.deep.equal({ a: { b: ['c', 'd'] } });
+ expect(Qs.parse('a[>=]=25')).to.deep.equal({ a: { '>=': '25' } });
+ done();
+ });
+
+ it('allows to specify array indices', function (done) {
+
+ expect(Qs.parse('a[1]=c&a[0]=b&a[2]=d')).to.deep.equal({ a: ['b', 'c', 'd'] });
+ expect(Qs.parse('a[1]=c&a[0]=b')).to.deep.equal({ a: ['b', 'c'] });
+ expect(Qs.parse('a[1]=c')).to.deep.equal({ a: ['c'] });
+ done();
+ });
+
+ it('limits specific array indices to 20', function (done) {
+
+ expect(Qs.parse('a[20]=a')).to.deep.equal({ a: ['a'] });
+ expect(Qs.parse('a[21]=a')).to.deep.equal({ a: { '21': 'a' } });
+ done();
+ });
+
+ it('supports keys that begin with a number', function (done) {
+
+ expect(Qs.parse('a[12b]=c')).to.deep.equal({ a: { '12b': 'c' } });
+ done();
+ });
+
+ it('supports encoded = signs', function (done) {
+
+ expect(Qs.parse('he%3Dllo=th%3Dere')).to.deep.equal({ 'he=llo': 'th=ere' });
+ done();
+ });
+
+ it('is ok with url encoded strings', function (done) {
+
+ expect(Qs.parse('a[b%20c]=d')).to.deep.equal({ a: { 'b c': 'd' } });
+ expect(Qs.parse('a[b]=c%20d')).to.deep.equal({ a: { b: 'c d' } });
+ done();
+ });
+
+ it('allows brackets in the value', function (done) {
+
+ expect(Qs.parse('pets=["tobi"]')).to.deep.equal({ pets: '["tobi"]' });
+ expect(Qs.parse('operators=[">=", "<="]')).to.deep.equal({ operators: '[">=", "<="]' });
+ done();
+ });
+
+ it('allows empty values', function (done) {
+
+ expect(Qs.parse('')).to.deep.equal({});
+ expect(Qs.parse(null)).to.deep.equal({});
+ expect(Qs.parse(undefined)).to.deep.equal({});
+ done();
+ });
+
+ it('transforms arrays to objects', function (done) {
+
+ expect(Qs.parse('foo[0]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
+ expect(Qs.parse('foo[bad]=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
+ expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
+ expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
+ expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
+ expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({foo: [ {a: 'a', b: 'b'}, {a: 'aa', b: 'bb'} ]});
+ done();
+ });
+
+ it('can add keys to objects', function (done) {
+
+ expect(Qs.parse('a[b]=c&a=d')).to.deep.equal({ a: { b: 'c', d: true } });
+ done();
+ });
+
+ it('correctly prunes undefined values when converting an array to an object', function (done) {
+
+ expect(Qs.parse('a[2]=b&a[99999999]=c')).to.deep.equal({ a: { '2': 'b', '99999999': 'c' } });
+ done();
+ });
+
+ it('supports malformed uri characters', function (done) {
+
+ expect(Qs.parse('{%:%}')).to.deep.equal({ '{%:%}': '' });
+ expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' });
+ done();
+ });
+
+ it('doesn\'t produce empty keys', function (done) {
+
+ expect(Qs.parse('_r=1&')).to.deep.equal({ '_r': '1' });
+ done();
+ });
+
+ it('cannot override prototypes', function (done) {
+
+ var obj = Qs.parse('hasOwnProperty=bad&toString=bad&bad[toString]=bad&constructor=bad');
+ expect(typeof obj.toString).to.equal('function');
+ expect(typeof obj.bad.toString).to.equal('function');
+ expect(typeof obj.constructor).to.equal('function');
+ expect(typeof obj.hasOwnProperty).to.equal('function');
+ done();
+ });
+
+ it('cannot access Object prototype', function (done) {
+
+ Qs.parse('constructor[prototype][bad]=bad');
+ Qs.parse('bad[constructor][prototype][bad]=bad');
+ expect(typeof Object.prototype.bad).to.equal('undefined');
+ done();
+ });
+
+ it('parses arrays of objects', function (done) {
+
+ expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
+ expect(Qs.parse('a[0][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
+ done();
+ });
+
+ it('allows for empty strings in arrays', function (done) {
+
+ expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] });
+ expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]=')).to.deep.equal({ a: ['b', '', 'c', ''] });
+ expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] });
+ done();
+ });
+
+ it('compacts sparse arrays', function (done) {
+
+ expect(Qs.parse('a[10]=1&a[2]=2')).to.deep.equal({ a: ['2', '1'] });
+ done();
+ });
+
+ it('parses semi-parsed strings', function (done) {
+
+ expect(Qs.parse({ 'a[b]': 'c' })).to.deep.equal({ a: { b: 'c' } });
+ expect(Qs.parse({ 'a[b]': 'c', 'a[d]': 'e' })).to.deep.equal({ a: { b: 'c', d: 'e' } });
+ done();
+ });
+
+ it('parses buffers correctly', function (done) {
+
+ var b = new Buffer('test');
+ expect(Qs.parse({ a: b })).to.deep.equal({ a: b });
+ done();
+ });
+
+ it('continues parsing when no parent is found', function (done) {
+
+ expect(Qs.parse('[]&a=b')).to.deep.equal({ '0': '', a: 'b' });
+ expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' });
+ done();
+ });
+
+ it('does not error when parsing a very long array', function (done) {
+
+ var str = 'a[]=a';
+ while (Buffer.byteLength(str) < 128 * 1024) {
+ str += '&' + str;
+ }
+
+ expect(function () {
+
+ Qs.parse(str);
+ }).to.not.throw();
+
+ done();
+ });
+
+ it('should not throw when a native prototype has an enumerable property', { parallel: false }, function (done) {
+
+ Object.prototype.crash = '';
+ Array.prototype.crash = '';
+ expect(Qs.parse.bind(null, 'a=b')).to.not.throw();
+ expect(Qs.parse('a=b')).to.deep.equal({ a: 'b' });
+ expect(Qs.parse.bind(null, 'a[][b]=c')).to.not.throw();
+ expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
+ delete Object.prototype.crash;
+ delete Array.prototype.crash;
+ done();
+ });
+
+ it('parses a string with an alternative string delimiter', function (done) {
+
+ expect(Qs.parse('a=b;c=d', { delimiter: ';' })).to.deep.equal({ a: 'b', c: 'd' });
+ done();
+ });
+
+ it('parses a string with an alternative RegExp delimiter', function (done) {
+
+ expect(Qs.parse('a=b; c=d', { delimiter: /[;,] */ })).to.deep.equal({ a: 'b', c: 'd' });
+ done();
+ });
+
+ it('does not use non-splittable objects as delimiters', function (done) {
+
+ expect(Qs.parse('a=b&c=d', { delimiter: true })).to.deep.equal({ a: 'b', c: 'd' });
+ done();
+ });
+
+ it('allows overriding parameter limit', function (done) {
+
+ expect(Qs.parse('a=b&c=d', { parameterLimit: 1 })).to.deep.equal({ a: 'b' });
+ done();
+ });
+
+ it('allows setting the parameter limit to Infinity', function (done) {
+
+ expect(Qs.parse('a=b&c=d', { parameterLimit: Infinity })).to.deep.equal({ a: 'b', c: 'd' });
+ done();
+ });
+
+ it('allows overriding array limit', function (done) {
+
+ expect(Qs.parse('a[0]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '0': 'b' } });
+ expect(Qs.parse('a[-1]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '-1': 'b' } });
+ expect(Qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 })).to.deep.equal({ a: { '0': 'b', '1': 'c' } });
+ done();
+ });
+
+ it('parses an object', function (done) {
+
+ var input = {
+ 'user[name]': {'pop[bob]': 3},
+ 'user[email]': null
+ };
+
+ var expected = {
+ 'user': {
+ 'name': {'pop[bob]': 3},
+ 'email': null
+ }
+ };
+
+ var result = Qs.parse(input);
+
+ expect(result).to.deep.equal(expected);
+ done();
+ });
+
+ it('parses an object and not child values', function (done) {
+
+ var input = {
+ 'user[name]': {'pop[bob]': { 'test': 3 }},
+ 'user[email]': null
+ };
+
+ var expected = {
+ 'user': {
+ 'name': {'pop[bob]': { 'test': 3 }},
+ 'email': null
+ }
+ };
+
+ var result = Qs.parse(input);
+
+ expect(result).to.deep.equal(expected);
+ done();
+ });
+
+ it('does not blow up when Buffer global is missing', function (done) {
+
+ var tempBuffer = global.Buffer;
+ delete global.Buffer;
+ var result = Qs.parse('a=b&c=d');
+ global.Buffer = tempBuffer;
+ expect(result).to.deep.equal({ a: 'b', c: 'd' });
+ done();
+ });
+
+ it('does not crash when using invalid dot notation', function (done) {
+
+ expect(Qs.parse('roomInfoList[0].childrenAges[0]=15&roomInfoList[0].numberOfAdults=2')).to.deep.equal({ roomInfoList: [['15', '2']] });
+ done();
+ });
+
+ it('does not crash when parsing circular references', function (done) {
+
+ var a = {};
+ a.b = a;
+
+ var parsed;
+
+ expect(function () {
+
+ parsed = Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
+ }).to.not.throw();
+
+ expect(parsed).to.contain('foo');
+ expect(parsed.foo).to.contain('bar', 'baz');
+ expect(parsed.foo.bar).to.equal('baz');
+ expect(parsed.foo.baz).to.deep.equal(a);
+ done();
+ });
+
+ it('parses plain objects correctly', function (done) {
+
+ var a = Object.create(null);
+ a.b = 'c';
+
+ expect(Qs.parse(a)).to.deep.equal({ b: 'c' });
+ var result = Qs.parse({ a: a });
+ expect(result).to.contain('a');
+ expect(result.a).to.deep.equal(a);
+ done();
+ });
+
+ it('parses dates correctly', function (done) {
+
+ var now = new Date();
+ expect(Qs.parse({ a: now })).to.deep.equal({ a: now });
+ done();
+ });
+
+ it('parses regular expressions correctly', function (done) {
+
+ var re = /^test$/;
+ expect(Qs.parse({ a: re })).to.deep.equal({ a: re });
+ done();
+ });
+});
--- /dev/null
+/* eslint no-extend-native:0 */
+// Load modules
+
+var Code = require('code');
+var Lab = require('lab');
+var Qs = require('../');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var lab = exports.lab = Lab.script();
+var expect = Code.expect;
+var describe = lab.experiment;
+var it = lab.test;
+
+
+describe('stringify()', function () {
+
+ it('stringifies a querystring object', function (done) {
+
+ expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
+ expect(Qs.stringify({ a: 1 })).to.equal('a=1');
+ expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2');
+ done();
+ });
+
+ it('stringifies a nested object', function (done) {
+
+ expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
+ expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e');
+ done();
+ });
+
+ it('stringifies an array value', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d');
+ done();
+ });
+
+ it('omits array indices when asked', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false })).to.equal('a=b&a=c&a=d');
+ done();
+ });
+
+ it('stringifies a nested array value', function (done) {
+
+ expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
+ done();
+ });
+
+ it('stringifies an object inside an array', function (done) {
+
+ expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c');
+ expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1');
+ done();
+ });
+
+ it('does not omit object keys when indices = false', function (done) {
+
+ expect(Qs.stringify({ a: [{ b: 'c' }] }, { indices: false })).to.equal('a%5Bb%5D=c');
+ done();
+ });
+
+ it('uses indices notation for arrays when indices=true', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c'] }, { indices: true })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
+ done();
+ });
+
+ it('uses indices notation for arrays when no arrayFormat is specified', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
+ done();
+ });
+
+ it('uses indices notation for arrays when no arrayFormat=indices', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
+ done();
+ });
+
+ it('uses repeat notation for arrays when no arrayFormat=repeat', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).to.equal('a=b&a=c');
+ done();
+ });
+
+ it('uses brackets notation for arrays when no arrayFormat=brackets', function (done) {
+
+ expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).to.equal('a%5B%5D=b&a%5B%5D=c');
+ done();
+ });
+
+ it('stringifies a complicated object', function (done) {
+
+ expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e');
+ done();
+ });
+
+ it('stringifies an empty value', function (done) {
+
+ expect(Qs.stringify({ a: '' })).to.equal('a=');
+ expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b=');
+ expect(Qs.stringify({ a: null })).to.equal('a=');
+ expect(Qs.stringify({ a: { b: null } })).to.equal('a%5Bb%5D=');
+ done();
+ });
+
+ it('stringifies an empty object', function (done) {
+
+ var obj = Object.create(null);
+ obj.a = 'b';
+ expect(Qs.stringify(obj)).to.equal('a=b');
+ done();
+ });
+
+ it('returns an empty string for invalid input', function (done) {
+
+ expect(Qs.stringify(undefined)).to.equal('');
+ expect(Qs.stringify(false)).to.equal('');
+ expect(Qs.stringify(null)).to.equal('');
+ expect(Qs.stringify('')).to.equal('');
+ done();
+ });
+
+ it('stringifies an object with an empty object as a child', function (done) {
+
+ var obj = {
+ a: Object.create(null)
+ };
+
+ obj.a.b = 'c';
+ expect(Qs.stringify(obj)).to.equal('a%5Bb%5D=c');
+ done();
+ });
+
+ it('drops keys with a value of undefined', function (done) {
+
+ expect(Qs.stringify({ a: undefined })).to.equal('');
+ expect(Qs.stringify({ a: { b: undefined, c: null } })).to.equal('a%5Bc%5D=');
+ done();
+ });
+
+ it('url encodes values', function (done) {
+
+ expect(Qs.stringify({ a: 'b c' })).to.equal('a=b%20c');
+ done();
+ });
+
+ it('stringifies a date', function (done) {
+
+ var now = new Date();
+ var str = 'a=' + encodeURIComponent(now.toISOString());
+ expect(Qs.stringify({ a: now })).to.equal(str);
+ done();
+ });
+
+ it('stringifies the weird object from qs', function (done) {
+
+ expect(Qs.stringify({ 'my weird field': 'q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F');
+ done();
+ });
+
+ it('skips properties that are part of the object prototype', function (done) {
+
+ Object.prototype.crash = 'test';
+ expect(Qs.stringify({ a: 'b'})).to.equal('a=b');
+ expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
+ delete Object.prototype.crash;
+ done();
+ });
+
+ it('stringifies boolean values', function (done) {
+
+ expect(Qs.stringify({ a: true })).to.equal('a=true');
+ expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true');
+ expect(Qs.stringify({ b: false })).to.equal('b=false');
+ expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false');
+ done();
+ });
+
+ it('stringifies buffer values', function (done) {
+
+ expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test');
+ expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test');
+ done();
+ });
+
+ it('stringifies an object using an alternative delimiter', function (done) {
+
+ expect(Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).to.equal('a=b;c=d');
+ done();
+ });
+
+ it('doesn\'t blow up when Buffer global is missing', function (done) {
+
+ var tempBuffer = global.Buffer;
+ delete global.Buffer;
+ expect(Qs.stringify({ a: 'b', c: 'd' })).to.equal('a=b&c=d');
+ global.Buffer = tempBuffer;
+ done();
+ });
+});
--- /dev/null
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+node_modules
+npm-debug.log
\ No newline at end of file
--- /dev/null
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
--- /dev/null
+Copyright 2012 Michael Hart (michael.hart.au@gmail.com)
+
+This project is free software released under the MIT license:
+http://www.opensource.org/licenses/mit-license.php
--- /dev/null
+# Decode streams into strings The Right Way(tm)
+
+```javascript
+var fs = require('fs')
+var zlib = require('zlib')
+var strs = require('stringstream')
+
+var utf8Stream = fs.createReadStream('massiveLogFile.gz')
+ .pipe(zlib.createGunzip())
+ .pipe(strs('utf8'))
+```
+
+No need to deal with `setEncoding()` weirdness, just compose streams
+like they were supposed to be!
+
+Handles input and output encoding:
+
+```javascript
+// Stream from utf8 to hex to base64... Why not, ay.
+var hex64Stream = fs.createReadStream('myFile')
+ .pipe(strs('utf8', 'hex'))
+ .pipe(strs('hex', 'base64'))
+```
+
+Also deals with `base64` output correctly by aligning each emitted data
+chunk so that there are no dangling `=` characters:
+
+```javascript
+var stream = fs.createReadStream('myFile').pipe(strs('base64'))
+
+var base64Str = ''
+
+stream.on('data', function(data) { base64Str += data })
+stream.on('end', function() {
+ console.log('My base64 encoded file is: ' + base64Str) // Wouldn't work with setEncoding()
+ console.log('Original file is: ' + new Buffer(base64Str, 'base64'))
+})
+```
--- /dev/null
+var fs = require('fs')
+var zlib = require('zlib')
+var strs = require('stringstream')
+
+var utf8Stream = fs.createReadStream('massiveLogFile.gz')
+ .pipe(zlib.createGunzip())
+ .pipe(strs('utf8'))
+
+utf8Stream.pipe(process.stdout)
+
+// Stream from utf8 to hex to base64... Why not, ay.
+var hex64Stream = fs.createReadStream('myFile')
+ .pipe(strs('utf8', 'hex'))
+ .pipe(strs('hex', 'base64'))
+
+hex64Stream.pipe(process.stdout)
+
+// Deals with base64 correctly by aligning chunks
+var stream = fs.createReadStream('myFile').pipe(strs('base64'))
+
+var base64Str = ''
+
+stream.on('data', function(data) { base64Str += data })
+stream.on('end', function() {
+ console.log('My base64 encoded file is: ' + base64Str) // Wouldn't work with setEncoding()
+ console.log('Original file is: ' + new Buffer(base64Str, 'base64'))
+})
--- /dev/null
+{
+ "name": "stringstream",
+ "version": "0.0.4",
+ "description": "Encode and decode streams into string streams",
+ "author": {
+ "name": "Michael Hart",
+ "email": "michael.hart.au@gmail.com",
+ "url": "http://github.com/mhart"
+ },
+ "main": "stringstream.js",
+ "keywords": [
+ "string",
+ "stream",
+ "base64",
+ "gzip"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/mhart/StringStream.git"
+ },
+ "license": "MIT",
+ "readme": "# Decode streams into strings The Right Way(tm)\n\n```javascript\nvar fs = require('fs')\nvar zlib = require('zlib')\nvar strs = require('stringstream')\n\nvar utf8Stream = fs.createReadStream('massiveLogFile.gz')\n .pipe(zlib.createGunzip())\n .pipe(strs('utf8'))\n```\n\nNo need to deal with `setEncoding()` weirdness, just compose streams\nlike they were supposed to be!\n\nHandles input and output encoding:\n\n```javascript\n// Stream from utf8 to hex to base64... Why not, ay.\nvar hex64Stream = fs.createReadStream('myFile')\n .pipe(strs('utf8', 'hex'))\n .pipe(strs('hex', 'base64'))\n```\n\nAlso deals with `base64` output correctly by aligning each emitted data\nchunk so that there are no dangling `=` characters:\n\n```javascript\nvar stream = fs.createReadStream('myFile').pipe(strs('base64'))\n\nvar base64Str = ''\n\nstream.on('data', function(data) { base64Str += data })\nstream.on('end', function() {\n console.log('My base64 encoded file is: ' + base64Str) // Wouldn't work with setEncoding()\n console.log('Original file is: ' + new Buffer(base64Str, 'base64'))\n})\n```\n",
+ "readmeFilename": "README.md",
+ "_id": "stringstream@0.0.4",
+ "dist": {
+ "shasum": "0f0e3423f942960b5692ac324a57dd093bc41a92",
+ "tarball": "http://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz"
+ },
+ "_npmVersion": "1.2.0",
+ "_npmUser": {
+ "name": "hichaelmart",
+ "email": "michael.hart.au@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "hichaelmart",
+ "email": "michael.hart.au@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "0f0e3423f942960b5692ac324a57dd093bc41a92",
+ "_resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz",
+ "_from": "stringstream@>=0.0.4 <0.1.0",
+ "bugs": {
+ "url": "https://github.com/mhart/StringStream/issues"
+ },
+ "homepage": "https://github.com/mhart/StringStream",
+ "scripts": {}
+}
--- /dev/null
+var util = require('util')
+var Stream = require('stream')
+var StringDecoder = require('string_decoder').StringDecoder
+
+module.exports = StringStream
+module.exports.AlignedStringDecoder = AlignedStringDecoder
+
+function StringStream(from, to) {
+ if (!(this instanceof StringStream)) return new StringStream(from, to)
+
+ Stream.call(this)
+
+ if (from == null) from = 'utf8'
+
+ this.readable = this.writable = true
+ this.paused = false
+ this.toEncoding = (to == null ? from : to)
+ this.fromEncoding = (to == null ? '' : from)
+ this.decoder = new AlignedStringDecoder(this.toEncoding)
+}
+util.inherits(StringStream, Stream)
+
+StringStream.prototype.write = function(data) {
+ if (!this.writable) {
+ var err = new Error('stream not writable')
+ err.code = 'EPIPE'
+ this.emit('error', err)
+ return false
+ }
+ if (this.fromEncoding) {
+ if (Buffer.isBuffer(data)) data = data.toString()
+ data = new Buffer(data, this.fromEncoding)
+ }
+ var string = this.decoder.write(data)
+ if (string.length) this.emit('data', string)
+ return !this.paused
+}
+
+StringStream.prototype.flush = function() {
+ if (this.decoder.flush) {
+ var string = this.decoder.flush()
+ if (string.length) this.emit('data', string)
+ }
+}
+
+StringStream.prototype.end = function() {
+ if (!this.writable && !this.readable) return
+ this.flush()
+ this.emit('end')
+ this.writable = this.readable = false
+ this.destroy()
+}
+
+StringStream.prototype.destroy = function() {
+ this.decoder = null
+ this.writable = this.readable = false
+ this.emit('close')
+}
+
+StringStream.prototype.pause = function() {
+ this.paused = true
+}
+
+StringStream.prototype.resume = function () {
+ if (this.paused) this.emit('drain')
+ this.paused = false
+}
+
+function AlignedStringDecoder(encoding) {
+ StringDecoder.call(this, encoding)
+
+ switch (this.encoding) {
+ case 'base64':
+ this.write = alignedWrite
+ this.alignedBuffer = new Buffer(3)
+ this.alignedBytes = 0
+ break
+ }
+}
+util.inherits(AlignedStringDecoder, StringDecoder)
+
+AlignedStringDecoder.prototype.flush = function() {
+ if (!this.alignedBuffer || !this.alignedBytes) return ''
+ var leftover = this.alignedBuffer.toString(this.encoding, 0, this.alignedBytes)
+ this.alignedBytes = 0
+ return leftover
+}
+
+function alignedWrite(buffer) {
+ var rem = (this.alignedBytes + buffer.length) % this.alignedBuffer.length
+ if (!rem && !this.alignedBytes) return buffer.toString(this.encoding)
+
+ var returnBuffer = new Buffer(this.alignedBytes + buffer.length - rem)
+
+ this.alignedBuffer.copy(returnBuffer, 0, 0, this.alignedBytes)
+ buffer.copy(returnBuffer, this.alignedBytes, 0, buffer.length - rem)
+
+ buffer.copy(this.alignedBuffer, 0, buffer.length - rem, buffer.length)
+ this.alignedBytes = rem
+
+ return returnBuffer.toString(this.encoding)
+}
--- /dev/null
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 2
--- /dev/null
+{
+ "passfail" : false,
+ "maxerr" : 100,
+
+ "browser" : false,
+ "node" : true,
+ "rhino" : false,
+ "couch" : false,
+ "wsh" : false,
+
+ "jquery" : false,
+ "prototypejs" : false,
+ "mootools" : false,
+ "dojo" : false,
+
+ "debug" : false,
+ "devel" : false,
+
+ "esnext" : true,
+ "strict" : true,
+ "globalstrict" : true,
+
+ "asi" : false,
+ "laxbreak" : false,
+ "bitwise" : true,
+ "boss" : false,
+ "curly" : true,
+ "eqeqeq" : false,
+ "eqnull" : true,
+ "evil" : false,
+ "expr" : false,
+ "forin" : false,
+ "immed" : true,
+ "lastsemic" : true,
+ "latedef" : false,
+ "loopfunc" : false,
+ "noarg" : true,
+ "regexp" : false,
+ "regexdash" : false,
+ "scripturl" : false,
+ "shadow" : false,
+ "supernew" : false,
+ "undef" : true,
+ "unused" : true,
+
+ "newcap" : true,
+ "noempty" : true,
+ "nonew" : true,
+ "nomen" : false,
+ "onevar" : false,
+ "onecase" : true,
+ "plusplus" : false,
+ "proto" : false,
+ "sub" : true,
+ "trailing" : true,
+ "white" : false,
+
+ "predef": [
+ "describe",
+ "it",
+ "before",
+ "beforeEach",
+ "after",
+ "afterEach",
+ "expect",
+ "setTimeout",
+ "clearTimeout"
+ ],
+ "maxlen": 0
+}
--- /dev/null
+.idea
+node_modules/
+.*.sw[nmop]
+npm-debug.log
--- /dev/null
+language: node_js
+node_js:
+- "0.10"
+- "0.12"
+- iojs
+matrix:
+ fast_finish: true
+ allow_failures:
+ - node_js: 0.11
--- /dev/null
+# Contributing
+
+This is the contribution guide for tough-cookie.
+
+## CLA
+
+As with all Salesforce open-source projects, tough-cookie requires a Contributor License Agreement to be signed by you (or your company).
+
+
--- /dev/null
+Copyright (c) 2015, Salesforce.com, 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:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+===
+
+The following exceptions apply:
+
+===
+
+`pubSufTest()` of generate-pubsuffix.js is in the public domain.
+
+ // Any copyright is dedicated to the Public Domain.
+ // http://creativecommons.org/publicdomain/zero/1.0/
+
+===
+
+`public-suffix.txt` was obtained from
+<https://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1>
+via <http://publicsuffix.org>.
+
+That file contains the usual Mozilla triple-license, for which this project uses it
+under the terms of the MPL 1.1:
+
+ // ***** BEGIN LICENSE BLOCK *****
+ // Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ //
+ // The contents of this file are subject to the Mozilla Public License Version
+ // 1.1 (the "License"); you may not use this file except in compliance with
+ // the License. You may obtain a copy of the License at
+ // http://www.mozilla.org/MPL/
+ //
+ // Software distributed under the License is distributed on an "AS IS" basis,
+ // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ // for the specific language governing rights and limitations under the
+ // License.
+ //
+ // The Original Code is the Public Suffix List.
+ //
+ // The Initial Developer of the Original Code is
+ // Jo Hermans <jo.hermans@gmail.com>.
+ // Portions created by the Initial Developer are Copyright (C) 2007
+ // the Initial Developer. All Rights Reserved.
+ //
+ // Contributor(s):
+ // Ruben Arakelyan <ruben@rubenarakelyan.com>
+ // Gervase Markham <gerv@gerv.net>
+ // Pamela Greene <pamg.bugs@gmail.com>
+ // David Triendl <david@triendl.name>
+ // Jothan Frakes <jothan@gmail.com>
+ // The kind representatives of many TLD registries
+ //
+ // Alternatively, the contents of this file may be used under the terms of
+ // either the GNU General Public License Version 2 or later (the "GPL"), or
+ // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ // in which case the provisions of the GPL or the LGPL are applicable instead
+ // of those above. If you wish to allow use of your version of this file only
+ // under the terms of either the GPL or the LGPL, and not to allow others to
+ // use your version of this file under the terms of the MPL, indicate your
+ // decision by deleting the provisions above and replace them with the notice
+ // and other provisions required by the GPL or the LGPL. If you do not delete
+ // the provisions above, a recipient may use your version of this file under
+ // the terms of any one of the MPL, the GPL or the LGPL.
+ //
+ // ***** END LICENSE BLOCK *****
--- /dev/null
+[RFC6265](https://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js
+
+[](https://travis-ci.org/SalesforceEng/tough-cookie)
+
+[](https://npmjs.org/package/tough-cookie)
+
+
+# Synopsis
+
+``` javascript
+var tough = require('tough-cookie'); // note: not 'cookie', 'cookies' or 'node-cookie'
+var Cookie = tough.Cookie;
+var cookie = Cookie.parse(header);
+cookie.value = 'somethingdifferent';
+header = cookie.toString();
+
+var cookiejar = new tough.CookieJar();
+cookiejar.setCookie(cookie, 'http://currentdomain.example.com/path', cb);
+// ...
+cookiejar.getCookies('http://example.com/otherpath',function(err,cookies) {
+ res.headers['cookie'] = cookies.join('; ');
+});
+```
+
+# Installation
+
+It's _so_ easy!
+
+`npm install tough-cookie`
+
+Requires `punycode`, which should get installed automatically for you. Note that node.js v0.6.2+ bundles punycode by default.
+
+Why the name? NPM modules `cookie`, `cookies` and `cookiejar` were already taken.
+
+# API
+
+tough
+=====
+
+Functions on the module you get from `require('tough-cookie')`. All can be used as pure functions and don't need to be "bound".
+
+**Note**: prior to 1.0.x, several of these functions took a `strict` parameter. This has since been removed from the API as it was no longer necessary.
+
+parseDate(string)
+-----------------
+
+Parse a cookie date string into a `Date`. Parses according to RFC6265 Section 5.1.1, not `Date.parse()`.
+
+formatDate(date)
+----------------
+
+Format a Date into a RFC1123 string (the RFC6265-recommended format).
+
+canonicalDomain(str)
+--------------------
+
+Transforms a domain-name into a canonical domain-name. The canonical domain-name is a trimmed, lowercased, stripped-of-leading-dot and optionally punycode-encoded domain-name (Section 5.1.2 of RFC6265). For the most part, this function is idempotent (can be run again on its output without ill effects).
+
+domainMatch(str,domStr[,canonicalize=true])
+-------------------------------------------
+
+Answers "does this real domain match the domain in a cookie?". The `str` is the "current" domain-name and the `domStr` is the "cookie" domain-name. Matches according to RFC6265 Section 5.1.3, but it helps to think of it as a "suffix match".
+
+The `canonicalize` parameter will run the other two paramters through `canonicalDomain` or not.
+
+defaultPath(path)
+-----------------
+
+Given a current request/response path, gives the Path apropriate for storing in a cookie. This is basically the "directory" of a "file" in the path, but is specified by Section 5.1.4 of the RFC.
+
+The `path` parameter MUST be _only_ the pathname part of a URI (i.e. excludes the hostname, query, fragment, etc.). This is the `.pathname` property of node's `uri.parse()` output.
+
+pathMatch(reqPath,cookiePath)
+-----------------------------
+
+Answers "does the request-path path-match a given cookie-path?" as per RFC6265 Section 5.1.4. Returns a boolean.
+
+This is essentially a prefix-match where `cookiePath` is a prefix of `reqPath`.
+
+parse(header)
+----------------------------
+
+alias for `Cookie.parse(header)`
+
+fromJSON(string)
+----------------
+
+alias for `Cookie.fromJSON(string)`
+
+getPublicSuffix(hostname)
+-------------------------
+
+Returns the public suffix of this hostname. The public suffix is the shortest domain-name upon which a cookie can be set. Returns `null` if the hostname cannot have cookies set for it.
+
+For example: `www.example.com` and `www.subdomain.example.com` both have public suffix `example.com`.
+
+For further information, see http://publicsuffix.org/. This module derives its list from that site.
+
+cookieCompare(a,b)
+------------------
+
+For use with `.sort()`, sorts a list of cookies into the recommended order given in the RFC (Section 5.4 step 2). Longest `.path`s go first, then sorted oldest to youngest.
+
+``` javascript
+var cookies = [ /* unsorted array of Cookie objects */ ];
+cookies = cookies.sort(cookieCompare);
+```
+
+permuteDomain(domain)
+---------------------
+
+Generates a list of all possible domains that `domainMatch()` the parameter. May be handy for implementing cookie stores.
+
+
+permutePath(path)
+-----------------
+
+Generates a list of all possible paths that `pathMatch()` the parameter. May be handy for implementing cookie stores.
+
+Cookie
+======
+
+Cookie.parse(header)
+-----------------------------------
+
+Parses a single Cookie or Set-Cookie HTTP header into a `Cookie` object. Returns `undefined` if the string can't be parsed.
+
+Here's how to process the Set-Cookie header(s) on a node HTTP/HTTPS response:
+
+``` javascript
+if (res.headers['set-cookie'] instanceof Array)
+ cookies = res.headers['set-cookie'].map(function (c) { return (Cookie.parse(c)); });
+else
+ cookies = [Cookie.parse(res.headers['set-cookie'])];
+```
+
+Cookie.fromJSON(string)
+-----------------------
+
+Convert a JSON string to a `Cookie` object. Does a `JSON.parse()` and converts the `.created`, `.lastAccessed` and `.expires` properties into `Date` objects.
+
+Properties
+==========
+
+ * _key_ - string - the name or key of the cookie (default "")
+ * _value_ - string - the value of the cookie (default "")
+ * _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to the string `"Infinity"`). See `setExpires()`
+ * _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. May also be set to strings `"Infinity"` and `"-Infinity"` for non-expiry and immediate-expiry, respectively. See `setMaxAge()`
+ * _domain_ - string - the `Domain=` attribute of the cookie
+ * _path_ - string - the `Path=` of the cookie
+ * _secure_ - boolean - the `Secure` cookie flag
+ * _httpOnly_ - boolean - the `HttpOnly` cookie flag
+ * _extensions_ - `Array` - any unrecognized cookie attributes as strings (even if equal-signs inside)
+
+After a cookie has been passed through `CookieJar.setCookie()` it will have the following additional attributes:
+
+ * _hostOnly_ - boolean - is this a host-only cookie (i.e. no Domain field was set, but was instead implied)
+ * _pathIsDefault_ - boolean - if true, there was no Path field on the cookie and `defaultPath()` was used to derive one.
+ * _created_ - `Date` - when this cookie was added to the jar
+ * _lastAccessed_ - `Date` - last time the cookie got accessed. Will affect cookie cleaning once implemented. Using `cookiejar.getCookies(...)` will update this attribute.
+
+Construction([{options}])
+------------
+
+Receives an options object that can contain any Cookie properties, uses the default for unspecified properties.
+
+.toString()
+-----------
+
+encode to a Set-Cookie header value. The Expires cookie field is set using `formatDate()`, but is omitted entirely if `.expires` is `Infinity`.
+
+.cookieString()
+---------------
+
+encode to a Cookie header value (i.e. the `.key` and `.value` properties joined with '=').
+
+.setExpires(String)
+-------------------
+
+sets the expiry based on a date-string passed through `parseDate()`. If parseDate returns `null` (i.e. can't parse this date string), `.expires` is set to `"Infinity"` (a string) is set.
+
+.setMaxAge(number)
+-------------------
+
+sets the maxAge in seconds. Coerces `-Infinity` to `"-Infinity"` and `Infinity` to `"Infinity"` so it JSON serializes correctly.
+
+.expiryTime([now=Date.now()])
+-----------------------------
+
+.expiryDate([now=Date.now()])
+-----------------------------
+
+expiryTime() Computes the absolute unix-epoch milliseconds that this cookie expires. expiryDate() works similarly, except it returns a `Date` object. Note that in both cases the `now` parameter should be milliseconds.
+
+Max-Age takes precedence over Expires (as per the RFC). The `.created` attribute -- or, by default, the `now` paramter -- is used to offset the `.maxAge` attribute.
+
+If Expires (`.expires`) is set, that's returned.
+
+Otherwise, `expiryTime()` returns `Infinity` and `expiryDate()` returns a `Date` object for "Tue, 19 Jan 2038 03:14:07 GMT" (latest date that can be expressed by a 32-bit `time_t`; the common limit for most user-agents).
+
+.TTL([now=Date.now()])
+---------
+
+compute the TTL relative to `now` (milliseconds). The same precedence rules as for `expiryTime`/`expiryDate` apply.
+
+The "number" `Infinity` is returned for cookies without an explicit expiry and `0` is returned if the cookie is expired. Otherwise a time-to-live in milliseconds is returned.
+
+.canonicalizedDoman()
+---------------------
+
+.cdomain()
+----------
+
+return the canonicalized `.domain` field. This is lower-cased and punycode (RFC3490) encoded if the domain has any non-ASCII characters.
+
+.validate()
+-----------
+
+Status: *IN PROGRESS*. Works for a few things, but is by no means comprehensive.
+
+validates cookie attributes for semantic correctness. Useful for "lint" checking any Set-Cookie headers you generate. For now, it returns a boolean, but eventually could return a reason string -- you can future-proof with this construct:
+
+``` javascript
+if (cookie.validate() === true) {
+ // it's tasty
+} else {
+ // yuck!
+}
+```
+
+CookieJar
+=========
+
+Construction([store = new MemoryCookieStore()][, rejectPublicSuffixes])
+------------
+
+Simply use `new CookieJar()`. If you'd like to use a custom store, pass that to the constructor otherwise a `MemoryCookieStore` will be created and used.
+
+
+Attributes
+----------
+
+ * _rejectPublicSuffixes_ - boolean - reject cookies with domains like "com" and "co.uk" (default: `true`)
+
+Since eventually this module would like to support database/remote/etc. CookieJars, continuation passing style is used for CookieJar methods.
+
+.setCookie(cookieOrString, currentUrl, [{options},] cb(err,cookie))
+-------------------------------------------------------------------
+
+Attempt to set the cookie in the cookie jar. If the operation fails, an error will be given to the callback `cb`, otherwise the cookie is passed through. The cookie will have updated `.created`, `.lastAccessed` and `.hostOnly` properties.
+
+The `options` object can be omitted and can have the following properties:
+
+ * _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects HttpOnly cookies.
+ * _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`.
+ * _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies
+ * _ignoreError_ - boolean - default `false` - silently ignore things like parse errors and invalid domains. CookieStore errors aren't ignored by this option.
+
+As per the RFC, the `.hostOnly` property is set if there was no "Domain=" parameter in the cookie string (or `.domain` was null on the Cookie object). The `.domain` property is set to the fully-qualified hostname of `currentUrl` in this case. Matching this cookie requires an exact hostname match (not a `domainMatch` as per usual).
+
+.setCookieSync(cookieOrString, currentUrl, [{options}])
+-------------------------------------------------------
+
+Synchronous version of `setCookie`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
+
+.storeCookie(cookie, [{options},] cb(err,cookie))
+-------------------------------------------------
+
+__REMOVED__ removed in lieu of the CookieStore API below
+
+.getCookies(currentUrl, [{options},] cb(err,cookies))
+-----------------------------------------------------
+
+Retrieve the list of cookies that can be sent in a Cookie header for the current url.
+
+If an error is encountered, that's passed as `err` to the callback, otherwise an `Array` of `Cookie` objects is passed. The array is sorted with `cookieCompare()` unless the `{sort:false}` option is given.
+
+The `options` object can be omitted and can have the following properties:
+
+ * _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects HttpOnly cookies.
+ * _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`.
+ * _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies
+ * _expire_ - boolean - default `true` - perform expiry-time checking of cookies and asynchronously remove expired cookies from the store. Using `false` will return expired cookies and **not** remove them from the store (which is useful for replaying Set-Cookie headers, potentially).
+ * _allPaths_ - boolean - default `false` - if `true`, do not scope cookies by path. The default uses RFC-compliant path scoping. **Note**: may not be supported by the CookieStore `fetchCookies` function (the default MemoryCookieStore supports it).
+
+The `.lastAccessed` property of the returned cookies will have been updated.
+
+.getCookiesSync(currentUrl, [{options}])
+----------------------------------------
+
+Synchronous version of `getCookies`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
+
+.getCookieString(...)
+---------------------
+
+Accepts the same options as `.getCookies()` but passes a string suitable for a Cookie header rather than an array to the callback. Simply maps the `Cookie` array via `.cookieString()`.
+
+.getCookieStringSync(...)
+-------------------------
+
+Synchronous version of `getCookieString`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
+
+.getSetCookieStrings(...)
+-------------------------
+
+Returns an array of strings suitable for **Set-Cookie** headers. Accepts the same options as `.getCookies()`. Simply maps the cookie array via `.toString()`.
+
+.getSetCookieStringsSync(...)
+-----------------------------
+
+Synchronous version of `getSetCookieStrings`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
+
+Store
+=====
+
+Base class for CookieJar stores.
+
+# CookieStore API
+
+The storage model for each `CookieJar` instance can be replaced with a custom implementation. The default is `MemoryCookieStore` which can be found in the `lib/memstore.js` file. The API uses continuation-passing-style to allow for asynchronous stores.
+
+Stores should inherit from the base `Store` class, which is available as `require('tough-cookie').Store`. Stores are asynchronous by default, but if `store.synchronous` is set, then the `*Sync` methods on the CookieJar can be used.
+
+All `domain` parameters will have been normalized before calling.
+
+The Cookie store must have all of the following methods.
+
+store.findCookie(domain, path, key, cb(err,cookie))
+---------------------------------------------------
+
+Retrieve a cookie with the given domain, path and key (a.k.a. name). The RFC maintains that exactly one of these cookies should exist in a store. If the store is using versioning, this means that the latest/newest such cookie should be returned.
+
+Callback takes an error and the resulting `Cookie` object. If no cookie is found then `null` MUST be passed instead (i.e. not an error).
+
+store.findCookies(domain, path, cb(err,cookies))
+------------------------------------------------
+
+Locates cookies matching the given domain and path. This is most often called in the context of `cookiejar.getCookies()` above.
+
+If no cookies are found, the callback MUST be passed an empty array.
+
+The resulting list will be checked for applicability to the current request according to the RFC (domain-match, path-match, http-only-flag, secure-flag, expiry, etc.), so it's OK to use an optimistic search algorithm when implementing this method. However, the search algorithm used SHOULD try to find cookies that `domainMatch()` the domain and `pathMatch()` the path in order to limit the amount of checking that needs to be done.
+
+As of version 0.9.12, the `allPaths` option to `cookiejar.getCookies()` above will cause the path here to be `null`. If the path is `null`, path-matching MUST NOT be performed (i.e. domain-matching only).
+
+store.putCookie(cookie, cb(err))
+--------------------------------
+
+Adds a new cookie to the store. The implementation SHOULD replace any existing cookie with the same `.domain`, `.path`, and `.key` properties -- depending on the nature of the implementation, it's possible that between the call to `fetchCookie` and `putCookie` that a duplicate `putCookie` can occur.
+
+The `cookie` object MUST NOT be modified; the caller will have already updated the `.creation` and `.lastAccessed` properties.
+
+Pass an error if the cookie cannot be stored.
+
+store.updateCookie(oldCookie, newCookie, cb(err))
+-------------------------------------------------
+
+Update an existing cookie. The implementation MUST update the `.value` for a cookie with the same `domain`, `.path` and `.key`. The implementation SHOULD check that the old value in the store is equivalent to `oldCookie` - how the conflict is resolved is up to the store.
+
+The `.lastAccessed` property will always be different between the two objects and `.created` will always be the same. Stores MAY ignore or defer the `.lastAccessed` change at the cost of affecting how cookies are sorted (or selected for deletion).
+
+Stores may wish to optimize changing the `.value` of the cookie in the store versus storing a new cookie. If the implementation doesn't define this method a stub that calls `putCookie(newCookie,cb)` will be added to the store object.
+
+The `newCookie` and `oldCookie` objects MUST NOT be modified.
+
+Pass an error if the newCookie cannot be stored.
+
+store.removeCookie(domain, path, key, cb(err))
+----------------------------------------------
+
+Remove a cookie from the store (see notes on `findCookie` about the uniqueness constraint).
+
+The implementation MUST NOT pass an error if the cookie doesn't exist; only pass an error due to the failure to remove an existing cookie.
+
+store.removeCookies(domain, path, cb(err))
+------------------------------------------
+
+Removes matching cookies from the store. The `path` paramter is optional, and if missing means all paths in a domain should be removed.
+
+Pass an error ONLY if removing any existing cookies failed.
+
+# TODO
+
+ * _full_ RFC5890/RFC5891 canonicalization for domains in `cdomain()`
+ * the optional `punycode` requirement implements RFC3492, but RFC6265 requires RFC5891
+ * better tests for `validate()`?
+
+# Copyright and License
+
+(tl;dr: BSD-3-Clause with some MPL/1.1)
+
+```text
+ Copyright (c) 2015, Salesforce.com, 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:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+```
+
+Portions may be licensed under different licenses (in particular public-suffix.txt is MPL/1.1); please read the LICENSE file for full details.
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+var fs = require('fs');
+var assert = require('assert');
+var punycode = require('punycode');
+
+fs.readFile('./public-suffix.txt', 'utf8', function(err,string) {
+ if (err) {
+ throw err;
+ }
+ var lines = string.split("\n");
+ process.nextTick(function() {
+ processList(lines);
+ });
+});
+
+var index = {};
+
+var COMMENT = new RegExp('//.+');
+function processList(lines) {
+ while (lines.length) {
+ var line = lines.shift();
+ line = line.replace(COMMENT,'').trim();
+ if (!line) {
+ continue;
+ }
+ addToIndex(index,line);
+ }
+
+ pubSufTest();
+
+ var w = fs.createWriteStream('./lib/pubsuffix.js',{
+ flags: 'w',
+ encoding: 'utf8',
+ mode: parseInt('644',8)
+ });
+ w.on('end', process.exit);
+ w.write("/****************************************************\n");
+ w.write(" * AUTOMATICALLY GENERATED by generate-pubsuffix.js *\n");
+ w.write(" * DO NOT EDIT! *\n");
+ w.write(" ****************************************************/\n\n");
+
+ w.write('"use strict";\n\n');
+ w.write("var punycode = require('punycode');\n\n");
+
+ w.write("module.exports.getPublicSuffix = ");
+ w.write(getPublicSuffix.toString());
+ w.write(";\n\n");
+
+ w.write("// The following generated structure is used under the MPL version 1.1\n");
+ w.write("// See public-suffix.txt for more information\n\n");
+ w.write("var index = module.exports.index = Object.freeze(\n");
+ w.write(JSON.stringify(index));
+ w.write(");\n\n");
+ w.write("// END of automatically generated file\n");
+
+ w.end();
+}
+
+function addToIndex(index,line) {
+ var prefix = '';
+ if (line.replace(/^(!|\*\.)/)) {
+ prefix = RegExp.$1;
+ line = line.slice(prefix.length);
+ }
+ line = prefix + punycode.toASCII(line);
+
+ if (line.substr(0,1) == '!') {
+ index[line.substr(1)] = false;
+ } else {
+ index[line] = true;
+ }
+}
+
+// include the licence in the function since it gets written to pubsuffix.js
+function getPublicSuffix(domain) {
+ /*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+ if (!domain) {
+ return null;
+ }
+ if (domain.match(/^\./)) {
+ return null;
+ }
+ var asciiDomain = punycode.toASCII(domain);
+ var converted = false;
+ if (asciiDomain !== domain) {
+ domain = asciiDomain;
+ converted = true;
+ }
+ if (index[domain]) {
+ return null;
+ }
+
+ domain = domain.toLowerCase();
+ var parts = domain.split('.').reverse();
+
+ var suffix = '';
+ var suffixLen = 0;
+ for (var i=0; i<parts.length; i++) {
+ var part = parts[i];
+ var starstr = '*'+suffix;
+ var partstr = part+suffix;
+
+ if (index[starstr]) { // star rule matches
+ suffixLen = i+1;
+ if (index[partstr] === false) { // exception rule matches (NB: false, not undefined)
+ suffixLen--;
+ }
+ } else if (index[partstr]) { // exact match, not exception
+ suffixLen = i+1;
+ }
+
+ suffix = '.'+partstr;
+ }
+
+ if (index['*'+suffix]) { // *.domain exists (e.g. *.kyoto.jp for domain='kyoto.jp');
+ return null;
+ }
+
+ suffixLen = suffixLen || 1;
+ if (parts.length > suffixLen) {
+ var publicSuffix = parts.slice(0,suffixLen+1).reverse().join('.');
+ return converted ? punycode.toUnicode(publicSuffix) : publicSuffix;
+ }
+
+ return null;
+}
+
+function checkPublicSuffix(give,get) {
+ var got = getPublicSuffix(give);
+ assert.equal(got, get, give+' should be '+(get==null?'NULL':get)+' but got '+got);
+}
+
+// pubSufTest() was converted to JavaScript from http://mxr.mozilla.org/mozilla-central/source/netwerk/test/unit/data/test_psl.txt?raw=1
+function pubSufTest() {
+ // For this function-scope and this function-scope ONLY:
+ // Any copyright is dedicated to the Public Domain.
+ // http://creativecommons.org/publicdomain/zero/1.0/
+
+ // NULL input.
+ checkPublicSuffix(null, null);
+ // Mixed case.
+ checkPublicSuffix('COM', null);
+ checkPublicSuffix('example.COM', 'example.com');
+ checkPublicSuffix('WwW.example.COM', 'example.com');
+ // Leading dot.
+ checkPublicSuffix('.com', null);
+ checkPublicSuffix('.example', null);
+ checkPublicSuffix('.example.com', null);
+ checkPublicSuffix('.example.example', null);
+ // Unlisted TLD.
+ checkPublicSuffix('example', null);
+ checkPublicSuffix('example.example', 'example.example');
+ checkPublicSuffix('b.example.example', 'example.example');
+ checkPublicSuffix('a.b.example.example', 'example.example');
+ // Listed, but non-Internet, TLD.
+ //checkPublicSuffix('local', null);
+ //checkPublicSuffix('example.local', null);
+ //checkPublicSuffix('b.example.local', null);
+ //checkPublicSuffix('a.b.example.local', null);
+ // TLD with only 1 rule.
+ checkPublicSuffix('biz', null);
+ checkPublicSuffix('domain.biz', 'domain.biz');
+ checkPublicSuffix('b.domain.biz', 'domain.biz');
+ checkPublicSuffix('a.b.domain.biz', 'domain.biz');
+ // TLD with some 2-level rules.
+ checkPublicSuffix('com', null);
+ checkPublicSuffix('example.com', 'example.com');
+ checkPublicSuffix('b.example.com', 'example.com');
+ checkPublicSuffix('a.b.example.com', 'example.com');
+ checkPublicSuffix('uk.com', null);
+ checkPublicSuffix('example.uk.com', 'example.uk.com');
+ checkPublicSuffix('b.example.uk.com', 'example.uk.com');
+ checkPublicSuffix('a.b.example.uk.com', 'example.uk.com');
+ checkPublicSuffix('test.ac', 'test.ac');
+ // TLD with only 1 (wildcard) rule.
+ checkPublicSuffix('cy', null);
+ checkPublicSuffix('c.cy', null);
+ checkPublicSuffix('b.c.cy', 'b.c.cy');
+ checkPublicSuffix('a.b.c.cy', 'b.c.cy');
+ // More complex TLD.
+ checkPublicSuffix('jp', null);
+ checkPublicSuffix('test.jp', 'test.jp');
+ checkPublicSuffix('www.test.jp', 'test.jp');
+ checkPublicSuffix('ac.jp', null);
+ checkPublicSuffix('test.ac.jp', 'test.ac.jp');
+ checkPublicSuffix('www.test.ac.jp', 'test.ac.jp');
+ checkPublicSuffix('kyoto.jp', null);
+ checkPublicSuffix('test.kyoto.jp', 'test.kyoto.jp');
+ checkPublicSuffix('ide.kyoto.jp', null);
+ checkPublicSuffix('b.ide.kyoto.jp', 'b.ide.kyoto.jp');
+ checkPublicSuffix('a.b.ide.kyoto.jp', 'b.ide.kyoto.jp');
+ checkPublicSuffix('c.kobe.jp', null);
+ checkPublicSuffix('b.c.kobe.jp', 'b.c.kobe.jp');
+ checkPublicSuffix('a.b.c.kobe.jp', 'b.c.kobe.jp');
+ checkPublicSuffix('city.kobe.jp', 'city.kobe.jp');
+ checkPublicSuffix('www.city.kobe.jp', 'city.kobe.jp');
+ // TLD with a wildcard rule and exceptions.
+ checkPublicSuffix('ck', null);
+ checkPublicSuffix('test.ck', null);
+ checkPublicSuffix('b.test.ck', 'b.test.ck');
+ checkPublicSuffix('a.b.test.ck', 'b.test.ck');
+ checkPublicSuffix('www.ck', 'www.ck');
+ checkPublicSuffix('www.www.ck', 'www.ck');
+ // US K12.
+ checkPublicSuffix('us', null);
+ checkPublicSuffix('test.us', 'test.us');
+ checkPublicSuffix('www.test.us', 'test.us');
+ checkPublicSuffix('ak.us', null);
+ checkPublicSuffix('test.ak.us', 'test.ak.us');
+ checkPublicSuffix('www.test.ak.us', 'test.ak.us');
+ checkPublicSuffix('k12.ak.us', null);
+ checkPublicSuffix('test.k12.ak.us', 'test.k12.ak.us');
+ checkPublicSuffix('www.test.k12.ak.us', 'test.k12.ak.us');
+ // IDN labels.
+ checkPublicSuffix('食狮.com.cn', '食狮.com.cn');
+ checkPublicSuffix('食狮.公司.cn', '食狮.公司.cn');
+ checkPublicSuffix('www.食狮.公司.cn', '食狮.公司.cn');
+ checkPublicSuffix('shishi.公司.cn', 'shishi.公司.cn');
+ checkPublicSuffix('公司.cn', null);
+ checkPublicSuffix('食狮.中国', '食狮.中国');
+ checkPublicSuffix('www.食狮.中国', '食狮.中国');
+ checkPublicSuffix('shishi.中国', 'shishi.中国');
+ checkPublicSuffix('中国', null);
+ // Same as above, but punycoded.
+ checkPublicSuffix('xn--85x722f.com.cn', 'xn--85x722f.com.cn');
+ checkPublicSuffix('xn--85x722f.xn--55qx5d.cn', 'xn--85x722f.xn--55qx5d.cn');
+ checkPublicSuffix('www.xn--85x722f.xn--55qx5d.cn', 'xn--85x722f.xn--55qx5d.cn');
+ checkPublicSuffix('shishi.xn--55qx5d.cn', 'shishi.xn--55qx5d.cn');
+ checkPublicSuffix('xn--55qx5d.cn', null);
+ checkPublicSuffix('xn--85x722f.xn--fiqs8s', 'xn--85x722f.xn--fiqs8s');
+ checkPublicSuffix('www.xn--85x722f.xn--fiqs8s', 'xn--85x722f.xn--fiqs8s');
+ checkPublicSuffix('shishi.xn--fiqs8s', 'shishi.xn--fiqs8s');
+ checkPublicSuffix('xn--fiqs8s', null);
+}
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+var net = require('net');
+var urlParse = require('url').parse;
+var pubsuffix = require('./pubsuffix');
+var Store = require('./store').Store;
+
+var punycode;
+try {
+ punycode = require('punycode');
+} catch(e) {
+ console.warn("cookie: can't load punycode; won't use punycode for domain normalization");
+}
+
+var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
+
+// From RFC6265 S4.1.1
+// note that it excludes \x3B ";"
+var COOKIE_OCTET = /[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/;
+var COOKIE_OCTETS = new RegExp('^'+COOKIE_OCTET.source+'$');
+
+// Double quotes are part of the value (see: S4.1.1).
+// '\r', '\n' and '\0' should be treated as a terminator in the "relaxed" mode
+// (see: https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60)
+// '=' and ';' are attribute/values separators
+// (see: https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L64)
+var COOKIE_PAIR = /^([^=;]+)\s*=\s*(("?)[^\n\r\0]*\3)/;
+
+// RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"'
+// Note ';' is \x3B
+var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/;
+
+// Used for checking whether or not there is a trailing semi-colon
+var TRAILING_SEMICOLON = /;+$/;
+
+var DAY_OF_MONTH = /^(\d{1,2})[^\d]*$/;
+var TIME = /^(\d{1,2})[^\d]*:(\d{1,2})[^\d]*:(\d{1,2})[^\d]*$/;
+var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i;
+
+var MONTH_TO_NUM = {
+ jan:0, feb:1, mar:2, apr:3, may:4, jun:5,
+ jul:6, aug:7, sep:8, oct:9, nov:10, dec:11
+};
+var NUM_TO_MONTH = [
+ 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'
+];
+var NUM_TO_DAY = [
+ 'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
+];
+
+var YEAR = /^(\d{2}|\d{4})$/; // 2 to 4 digits
+
+var MAX_TIME = 2147483647000; // 31-bit max
+var MIN_TIME = 0; // 31-bit min
+
+var cookiesCreated = 0; // Number of cookies created in runtime
+
+
+// RFC6265 S5.1.1 date parser:
+function parseDate(str) {
+ if (!str) {
+ return;
+ }
+
+ /* RFC6265 S5.1.1:
+ * 2. Process each date-token sequentially in the order the date-tokens
+ * appear in the cookie-date
+ */
+ var tokens = str.split(DATE_DELIM);
+ if (!tokens) {
+ return;
+ }
+
+ var hour = null;
+ var minutes = null;
+ var seconds = null;
+ var day = null;
+ var month = null;
+ var year = null;
+
+ for (var i=0; i<tokens.length; i++) {
+ var token = tokens[i].trim();
+ if (!token.length) {
+ continue;
+ }
+
+ var result;
+
+ /* 2.1. If the found-time flag is not set and the token matches the time
+ * production, set the found-time flag and set the hour- value,
+ * minute-value, and second-value to the numbers denoted by the digits in
+ * the date-token, respectively. Skip the remaining sub-steps and continue
+ * to the next date-token.
+ */
+ if (seconds === null) {
+ result = TIME.exec(token);
+ if (result) {
+ hour = parseInt(result[1], 10);
+ minutes = parseInt(result[2], 10);
+ seconds = parseInt(result[3], 10);
+ /* RFC6265 S5.1.1.5:
+ * [fail if]
+ * * the hour-value is greater than 23,
+ * * the minute-value is greater than 59, or
+ * * the second-value is greater than 59.
+ */
+ if(hour > 23 || minutes > 59 || seconds > 59) {
+ return;
+ }
+
+ continue;
+ }
+ }
+
+ /* 2.2. If the found-day-of-month flag is not set and the date-token matches
+ * the day-of-month production, set the found-day-of- month flag and set
+ * the day-of-month-value to the number denoted by the date-token. Skip
+ * the remaining sub-steps and continue to the next date-token.
+ */
+ if (day === null) {
+ result = DAY_OF_MONTH.exec(token);
+ if (result) {
+ day = parseInt(result, 10);
+ /* RFC6265 S5.1.1.5:
+ * [fail if] the day-of-month-value is less than 1 or greater than 31
+ */
+ if(day < 1 || day > 31) {
+ return;
+ }
+ continue;
+ }
+ }
+
+ /* 2.3. If the found-month flag is not set and the date-token matches the
+ * month production, set the found-month flag and set the month-value to
+ * the month denoted by the date-token. Skip the remaining sub-steps and
+ * continue to the next date-token.
+ */
+ if (month === null) {
+ result = MONTH.exec(token);
+ if (result) {
+ month = MONTH_TO_NUM[result[1].toLowerCase()];
+ continue;
+ }
+ }
+
+ /* 2.4. If the found-year flag is not set and the date-token matches the year
+ * production, set the found-year flag and set the year-value to the number
+ * denoted by the date-token. Skip the remaining sub-steps and continue to
+ * the next date-token.
+ */
+ if (year === null) {
+ result = YEAR.exec(token);
+ if (result) {
+ year = parseInt(result[0], 10);
+ /* From S5.1.1:
+ * 3. If the year-value is greater than or equal to 70 and less
+ * than or equal to 99, increment the year-value by 1900.
+ * 4. If the year-value is greater than or equal to 0 and less
+ * than or equal to 69, increment the year-value by 2000.
+ */
+ if (70 <= year && year <= 99) {
+ year += 1900;
+ } else if (0 <= year && year <= 69) {
+ year += 2000;
+ }
+
+ if (year < 1601) {
+ return; // 5. ... the year-value is less than 1601
+ }
+ }
+ }
+ }
+
+ if (seconds === null || day === null || month === null || year === null) {
+ return; // 5. ... at least one of the found-day-of-month, found-month, found-
+ // year, or found-time flags is not set,
+ }
+
+ return new Date(Date.UTC(year, month, day, hour, minutes, seconds));
+}
+
+function formatDate(date) {
+ var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
+ var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
+ var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
+ var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
+ return NUM_TO_DAY[date.getUTCDay()] + ', ' +
+ d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
+ h+':'+m+':'+s+' GMT';
+}
+
+// S5.1.2 Canonicalized Host Names
+function canonicalDomain(str) {
+ if (str == null) {
+ return null;
+ }
+ str = str.trim().replace(/^\./,''); // S4.1.2.3 & S5.2.3: ignore leading .
+
+ // convert to IDN if any non-ASCII characters
+ if (punycode && /[^\u0001-\u007f]/.test(str)) {
+ str = punycode.toASCII(str);
+ }
+
+ return str.toLowerCase();
+}
+
+// S5.1.3 Domain Matching
+function domainMatch(str, domStr, canonicalize) {
+ if (str == null || domStr == null) {
+ return null;
+ }
+ if (canonicalize !== false) {
+ str = canonicalDomain(str);
+ domStr = canonicalDomain(domStr);
+ }
+
+ /*
+ * "The domain string and the string are identical. (Note that both the
+ * domain string and the string will have been canonicalized to lower case at
+ * this point)"
+ */
+ if (str == domStr) {
+ return true;
+ }
+
+ /* "All of the following [three] conditions hold:" (order adjusted from the RFC) */
+
+ /* "* The string is a host name (i.e., not an IP address)." */
+ if (net.isIP(str)) {
+ return false;
+ }
+
+ /* "* The domain string is a suffix of the string" */
+ var idx = str.indexOf(domStr);
+ if (idx <= 0) {
+ return false; // it's a non-match (-1) or prefix (0)
+ }
+
+ // e.g "a.b.c".indexOf("b.c") === 2
+ // 5 === 3+2
+ if (str.length !== domStr.length + idx) { // it's not a suffix
+ return false;
+ }
+
+ /* "* The last character of the string that is not included in the domain
+ * string is a %x2E (".") character." */
+ if (str.substr(idx-1,1) !== '.') {
+ return false;
+ }
+
+ return true;
+}
+
+
+// RFC6265 S5.1.4 Paths and Path-Match
+
+/*
+ * "The user agent MUST use an algorithm equivalent to the following algorithm
+ * to compute the default-path of a cookie:"
+ *
+ * Assumption: the path (and not query part or absolute uri) is passed in.
+ */
+function defaultPath(path) {
+ // "2. If the uri-path is empty or if the first character of the uri-path is not
+ // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
+ if (!path || path.substr(0,1) !== "/") {
+ return "/";
+ }
+
+ // "3. If the uri-path contains no more than one %x2F ("/") character, output
+ // %x2F ("/") and skip the remaining step."
+ if (path === "/") {
+ return path;
+ }
+
+ var rightSlash = path.lastIndexOf("/");
+ if (rightSlash === 0) {
+ return "/";
+ }
+
+ // "4. Output the characters of the uri-path from the first character up to,
+ // but not including, the right-most %x2F ("/")."
+ return path.slice(0, rightSlash);
+}
+
+/*
+ * "A request-path path-matches a given cookie-path if at least one of the
+ * following conditions holds:"
+ */
+function pathMatch(reqPath,cookiePath) {
+ // "o The cookie-path and the request-path are identical."
+ if (cookiePath === reqPath) {
+ return true;
+ }
+
+ var idx = reqPath.indexOf(cookiePath);
+ if (idx === 0) {
+ // "o The cookie-path is a prefix of the request-path, and the last
+ // character of the cookie-path is %x2F ("/")."
+ if (cookiePath.substr(-1) === "/") {
+ return true;
+ }
+
+ // " o The cookie-path is a prefix of the request-path, and the first
+ // character of the request-path that is not included in the cookie- path
+ // is a %x2F ("/") character."
+ if (reqPath.substr(cookiePath.length,1) === "/") {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function parse(str) {
+ str = str.trim();
+
+ // S4.1.1 Trailing semi-colons are not part of the specification.
+ var semiColonCheck = TRAILING_SEMICOLON.exec(str);
+ if (semiColonCheck) {
+ str = str.slice(0, semiColonCheck.index);
+ }
+
+ // We use a regex to parse the "name-value-pair" part of S5.2
+ var firstSemi = str.indexOf(';'); // S5.2 step 1
+ var result = COOKIE_PAIR.exec(firstSemi === -1 ? str : str.substr(0,firstSemi));
+
+ // Rx satisfies the "the name string is empty" and "lacks a %x3D ("=")"
+ // constraints as well as trimming any whitespace.
+ if (!result) {
+ return;
+ }
+
+ var c = new Cookie();
+ c.key = result[1].trim();
+ c.value = result[2].trim();
+
+ if (firstSemi === -1) {
+ return c;
+ }
+
+ // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string
+ // (including the %x3B (";") in question)." plus later on in the same section
+ // "discard the first ";" and trim".
+ var unparsed = str.slice(firstSemi).replace(/^\s*;\s*/,'').trim();
+
+ // "If the unparsed-attributes string is empty, skip the rest of these
+ // steps."
+ if (unparsed.length === 0) {
+ return c;
+ }
+
+ /*
+ * S5.2 says that when looping over the items "[p]rocess the attribute-name
+ * and attribute-value according to the requirements in the following
+ * subsections" for every item. Plus, for many of the individual attributes
+ * in S5.3 it says to use the "attribute-value of the last attribute in the
+ * cookie-attribute-list". Therefore, in this implementation, we overwrite
+ * the previous value.
+ */
+ var cookie_avs = unparsed.split(/\s*;\s*/);
+ while (cookie_avs.length) {
+ var av = cookie_avs.shift();
+ var av_sep = av.indexOf('=');
+ var av_key, av_value;
+
+ if (av_sep === -1) {
+ av_key = av;
+ av_value = null;
+ } else {
+ av_key = av.substr(0,av_sep);
+ av_value = av.substr(av_sep+1);
+ }
+
+ av_key = av_key.trim().toLowerCase();
+
+ if (av_value) {
+ av_value = av_value.trim();
+ }
+
+ switch(av_key) {
+ case 'expires': // S5.2.1
+ if (av_value) {
+ var exp = parseDate(av_value);
+ // "If the attribute-value failed to parse as a cookie date, ignore the
+ // cookie-av."
+ if (exp) {
+ // over and underflow not realistically a concern: V8's getTime() seems to
+ // store something larger than a 32-bit time_t (even with 32-bit node)
+ c.expires = exp;
+ }
+ }
+ break;
+
+ case 'max-age': // S5.2.2
+ if (av_value) {
+ // "If the first character of the attribute-value is not a DIGIT or a "-"
+ // character ...[or]... If the remainder of attribute-value contains a
+ // non-DIGIT character, ignore the cookie-av."
+ if (/^-?[0-9]+$/.test(av_value)) {
+ var delta = parseInt(av_value, 10);
+ // "If delta-seconds is less than or equal to zero (0), let expiry-time
+ // be the earliest representable date and time."
+ c.setMaxAge(delta);
+ }
+ }
+ break;
+
+ case 'domain': // S5.2.3
+ // "If the attribute-value is empty, the behavior is undefined. However,
+ // the user agent SHOULD ignore the cookie-av entirely."
+ if (av_value) {
+ // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
+ // (".") character."
+ var domain = av_value.trim().replace(/^\./, '');
+ if (domain) {
+ // "Convert the cookie-domain to lower case."
+ c.domain = domain.toLowerCase();
+ }
+ }
+ break;
+
+ case 'path': // S5.2.4
+ /*
+ * "If the attribute-value is empty or if the first character of the
+ * attribute-value is not %x2F ("/"):
+ * Let cookie-path be the default-path.
+ * Otherwise:
+ * Let cookie-path be the attribute-value."
+ *
+ * We'll represent the default-path as null since it depends on the
+ * context of the parsing.
+ */
+ c.path = av_value && av_value[0] === "/" ? av_value : null;
+ break;
+
+ case 'secure': // S5.2.5
+ /*
+ * "If the attribute-name case-insensitively matches the string "Secure",
+ * the user agent MUST append an attribute to the cookie-attribute-list
+ * with an attribute-name of Secure and an empty attribute-value."
+ */
+ c.secure = true;
+ break;
+
+ case 'httponly': // S5.2.6 -- effectively the same as 'secure'
+ c.httpOnly = true;
+ break;
+
+ default:
+ c.extensions = c.extensions || [];
+ c.extensions.push(av);
+ break;
+ }
+ }
+
+ // ensure a default date for sorting:
+ c.creation = new Date();
+ //NOTE: add runtime index for the cookieCompare() to resolve the situation when Date's precision is not enough .
+ //Store initial UTC time as well, so we will be able to determine if we need to fallback to the Date object.
+ c._creationRuntimeIdx = ++cookiesCreated;
+ c._initialCreationTime = c.creation.getTime();
+ return c;
+}
+
+function fromJSON(str) {
+ if (!str) {
+ return null;
+ }
+
+ var obj;
+ try {
+ obj = JSON.parse(str);
+ } catch (e) {
+ return null;
+ }
+
+ var c = new Cookie();
+ for (var i=0; i<numCookieProperties; i++) {
+ var prop = cookieProperties[i];
+ if (obj[prop] == null) {
+ continue;
+ }
+ if (prop === 'expires' ||
+ prop === 'creation' ||
+ prop === 'lastAccessed')
+ {
+ c[prop] = obj[prop] == "Infinity" ? "Infinity" : new Date(obj[prop]);
+ } else {
+ c[prop] = obj[prop];
+ }
+ }
+
+
+ // ensure a default date for sorting:
+ c.creation = c.creation || new Date();
+
+ return c;
+}
+
+/* Section 5.4 part 2:
+ * "* Cookies with longer paths are listed before cookies with
+ * shorter paths.
+ *
+ * * Among cookies that have equal-length path fields, cookies with
+ * earlier creation-times are listed before cookies with later
+ * creation-times."
+ */
+
+function cookieCompare(a,b) {
+ // descending for length: b CMP a
+ var deltaLen = (b.path ? b.path.length : 0) - (a.path ? a.path.length : 0);
+ if (deltaLen !== 0) {
+ return deltaLen;
+ }
+
+ var aTime = a.creation ? a.creation.getTime() : MAX_TIME;
+ var bTime = b.creation ? b.creation.getTime() : MAX_TIME;
+
+ // NOTE: if creation dates are equal and they were not modified from the outside,
+ // then use _creationRuntimeIdx for the comparison.
+ if(aTime === bTime && aTime === a._initialCreationTime && bTime === b._initialCreationTime) {
+ return a._creationRuntimeIdx - b._creationRuntimeIdx;
+ }
+
+ // ascending for time: a CMP b
+ return aTime - bTime;
+}
+
+// Gives the permutation of all possible domainMatch()es of a given domain. The
+// array is in shortest-to-longest order. Handy for indexing.
+function permuteDomain(domain) {
+ var pubSuf = pubsuffix.getPublicSuffix(domain);
+ if (!pubSuf) {
+ return null;
+ }
+ if (pubSuf == domain) {
+ return [domain];
+ }
+
+ var prefix = domain.slice(0,-(pubSuf.length+1)); // ".example.com"
+ var parts = prefix.split('.').reverse();
+ var cur = pubSuf;
+ var permutations = [cur];
+ while (parts.length) {
+ cur = parts.shift()+'.'+cur;
+ permutations.push(cur);
+ }
+ return permutations;
+}
+
+// Gives the permutation of all possible pathMatch()es of a given path. The
+// array is in longest-to-shortest order. Handy for indexing.
+function permutePath(path) {
+ if (path === '/') {
+ return ['/'];
+ }
+ if (path.lastIndexOf('/') === path.length-1) {
+ path = path.substr(0,path.length-1);
+ }
+ var permutations = [path];
+ while (path.length > 1) {
+ var lindex = path.lastIndexOf('/');
+ if (lindex === 0) {
+ break;
+ }
+ path = path.substr(0,lindex);
+ permutations.push(path);
+ }
+ permutations.push('/');
+ return permutations;
+}
+
+function getCookieContext(url) {
+ if(url instanceof Object)
+ return url;
+
+ // NOTE: decodeURI will throw on malformed URIs (see GH-32).
+ // Therefore, we will just skip decoding for such URIs.
+ try {
+ url = decodeURI(url);
+ }
+ catch(err) {
+ // Silently swallow error
+ }
+
+ return urlParse(url);
+}
+
+function Cookie (opts) {
+ if (typeof opts !== "object") {
+ return;
+ }
+ Object.keys(opts).forEach(function (key) {
+ if (Cookie.prototype.hasOwnProperty(key)) {
+ this[key] = opts[key] || Cookie.prototype[key];
+ }
+ }.bind(this));
+}
+
+Cookie.parse = parse;
+Cookie.fromJSON = fromJSON;
+
+Cookie.prototype.key = "";
+Cookie.prototype.value = "";
+
+// the order in which the RFC has them:
+Cookie.prototype.expires = "Infinity"; // coerces to literal Infinity
+Cookie.prototype.maxAge = null; // takes precedence over expires for TTL
+Cookie.prototype.domain = null;
+Cookie.prototype.path = null;
+Cookie.prototype.secure = false;
+Cookie.prototype.httpOnly = false;
+Cookie.prototype.extensions = null;
+
+// set by the CookieJar:
+Cookie.prototype.hostOnly = null; // boolean when set
+Cookie.prototype.pathIsDefault = null; // boolean when set
+Cookie.prototype.creation = null; // Date when set; defaulted by Cookie.parse
+Cookie.prototype._initialCreationTime = null; // Used to determine if cookie.creation was modified
+Cookie.prototype._creationRuntimeIdx = null; // Runtime index of the created cookie, used in cookieCompare()
+Cookie.prototype.lastAccessed = null; // Date when set
+
+var cookieProperties = Object.freeze(Object.keys(Cookie.prototype).map(function(p) {
+ if (p instanceof Function) {
+ return;
+ }
+ return p;
+}));
+var numCookieProperties = cookieProperties.length;
+
+Cookie.prototype.inspect = function inspect() {
+ var now = Date.now();
+ return 'Cookie="'+this.toString() +
+ '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') +
+ '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') +
+ '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') +
+ '"';
+};
+
+Cookie.prototype.validate = function validate() {
+ if (!COOKIE_OCTETS.test(this.value)) {
+ return false;
+ }
+ if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires,true)) {
+ return false;
+ }
+ if (this.maxAge != null && this.maxAge <= 0) {
+ return false; // "Max-Age=" non-zero-digit *DIGIT
+ }
+ if (this.path != null && !PATH_VALUE.test(this.path)) {
+ return false;
+ }
+
+ var cdomain = this.cdomain();
+ if (cdomain) {
+ if (cdomain.match(/\.$/)) {
+ return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this
+ }
+ var suffix = pubsuffix.getPublicSuffix(cdomain);
+ if (suffix == null) { // it's a public suffix
+ return false;
+ }
+ }
+ return true;
+};
+
+Cookie.prototype.setExpires = function setExpires(exp) {
+ if (exp instanceof Date) {
+ this.expires = exp;
+ } else {
+ this.expires = parseDate(exp) || "Infinity";
+ }
+};
+
+Cookie.prototype.setMaxAge = function setMaxAge(age) {
+ if (age === Infinity || age === -Infinity) {
+ this.maxAge = age.toString(); // so JSON.stringify() works
+ } else {
+ this.maxAge = age;
+ }
+};
+
+// gives Cookie header format
+Cookie.prototype.cookieString = function cookieString() {
+ var val = this.value;
+ if (val == null) {
+ val = '';
+ }
+ return this.key+'='+val;
+};
+
+// gives Set-Cookie header format
+Cookie.prototype.toString = function toString() {
+ var str = this.cookieString();
+
+ if (this.expires != Infinity) {
+ if (this.expires instanceof Date) {
+ str += '; Expires='+formatDate(this.expires);
+ } else {
+ str += '; Expires='+this.expires;
+ }
+ }
+
+ if (this.maxAge != null && this.maxAge != Infinity) {
+ str += '; Max-Age='+this.maxAge;
+ }
+
+ if (this.domain && !this.hostOnly) {
+ str += '; Domain='+this.domain;
+ }
+ if (this.path) {
+ str += '; Path='+this.path;
+ }
+
+ if (this.secure) {
+ str += '; Secure';
+ }
+ if (this.httpOnly) {
+ str += '; HttpOnly';
+ }
+ if (this.extensions) {
+ this.extensions.forEach(function(ext) {
+ str += '; '+ext;
+ });
+ }
+
+ return str;
+};
+
+// TTL() partially replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
+// elsewhere)
+// S5.3 says to give the "latest representable date" for which we use Infinity
+// For "expired" we use 0
+Cookie.prototype.TTL = function TTL(now) {
+ /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires
+ * attribute, the Max-Age attribute has precedence and controls the
+ * expiration date of the cookie.
+ * (Concurs with S5.3 step 3)
+ */
+ if (this.maxAge != null) {
+ return this.maxAge<=0 ? 0 : this.maxAge*1000;
+ }
+
+ var expires = this.expires;
+ if (expires != Infinity) {
+ if (!(expires instanceof Date)) {
+ expires = parseDate(expires) || Infinity;
+ }
+
+ if (expires == Infinity) {
+ return Infinity;
+ }
+
+ return expires.getTime() - (now || Date.now());
+ }
+
+ return Infinity;
+};
+
+// expiryTime() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
+// elsewhere)
+Cookie.prototype.expiryTime = function expiryTime(now) {
+ if (this.maxAge != null) {
+ var relativeTo = this.creation || now || new Date();
+ var age = (this.maxAge <= 0) ? -Infinity : this.maxAge*1000;
+ return relativeTo.getTime() + age;
+ }
+
+ if (this.expires == Infinity) {
+ return Infinity;
+ }
+ return this.expires.getTime();
+};
+
+// expiryDate() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
+// elsewhere), except it returns a Date
+Cookie.prototype.expiryDate = function expiryDate(now) {
+ var millisec = this.expiryTime(now);
+ if (millisec == Infinity) {
+ return new Date(MAX_TIME);
+ } else if (millisec == -Infinity) {
+ return new Date(MIN_TIME);
+ } else {
+ return new Date(millisec);
+ }
+};
+
+// This replaces the "persistent-flag" parts of S5.3 step 3
+Cookie.prototype.isPersistent = function isPersistent() {
+ return (this.maxAge != null || this.expires != Infinity);
+};
+
+// Mostly S5.1.2 and S5.2.3:
+Cookie.prototype.cdomain =
+Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() {
+ if (this.domain == null) {
+ return null;
+ }
+ return canonicalDomain(this.domain);
+};
+
+
+var memstore;
+function CookieJar(store, rejectPublicSuffixes) {
+ if (rejectPublicSuffixes != null) {
+ this.rejectPublicSuffixes = rejectPublicSuffixes;
+ }
+
+ if (!store) {
+ memstore = memstore || require('./memstore');
+ store = new memstore.MemoryCookieStore();
+ }
+ this.store = store;
+}
+CookieJar.prototype.store = null;
+CookieJar.prototype.rejectPublicSuffixes = true;
+var CAN_BE_SYNC = [];
+
+CAN_BE_SYNC.push('setCookie');
+CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
+ var err;
+ var context = getCookieContext(url);
+ if (options instanceof Function) {
+ cb = options;
+ options = {};
+ }
+
+ var host = canonicalDomain(context.hostname);
+
+ // S5.3 step 1
+ if (!(cookie instanceof Cookie)) {
+ cookie = Cookie.parse(cookie);
+ }
+ if (!cookie) {
+ err = new Error("Cookie failed to parse");
+ return cb(options.ignoreError ? null : err);
+ }
+
+ // S5.3 step 2
+ var now = options.now || new Date(); // will assign later to save effort in the face of errors
+
+ // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
+
+ // S5.3 step 4: NOOP; domain is null by default
+
+ // S5.3 step 5: public suffixes
+ if (this.rejectPublicSuffixes && cookie.domain) {
+ var suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
+ if (suffix == null) { // e.g. "com"
+ err = new Error("Cookie has domain set to a public suffix");
+ return cb(options.ignoreError ? null : err);
+ }
+ }
+
+ // S5.3 step 6:
+ if (cookie.domain) {
+ if (!domainMatch(host, cookie.cdomain(), false)) {
+ err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host);
+ return cb(options.ignoreError ? null : err);
+ }
+
+ if (cookie.hostOnly == null) { // don't reset if already set
+ cookie.hostOnly = false;
+ }
+
+ } else {
+ cookie.hostOnly = true;
+ cookie.domain = host;
+ }
+
+ //S5.2.4 If the attribute-value is empty or if the first character of the
+ //attribute-value is not %x2F ("/"):
+ //Let cookie-path be the default-path.
+ if (!cookie.path || cookie.path[0] !== '/') {
+ cookie.path = defaultPath(context.pathname);
+ cookie.pathIsDefault = true;
+ }
+
+ // S5.3 step 8: NOOP; secure attribute
+ // S5.3 step 9: NOOP; httpOnly attribute
+
+ // S5.3 step 10
+ if (options.http === false && cookie.httpOnly) {
+ err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
+ return cb(options.ignoreError ? null : err);
+ }
+
+ var store = this.store;
+
+ if (!store.updateCookie) {
+ store.updateCookie = function(oldCookie, newCookie, cb) {
+ this.putCookie(newCookie, cb);
+ };
+ }
+
+ function withCookie(err, oldCookie) {
+ if (err) {
+ return cb(err);
+ }
+
+ var next = function(err) {
+ if (err) {
+ return cb(err);
+ } else {
+ cb(null, cookie);
+ }
+ };
+
+ if (oldCookie) {
+ // S5.3 step 11 - "If the cookie store contains a cookie with the same name,
+ // domain, and path as the newly created cookie:"
+ if (options.http === false && oldCookie.httpOnly) { // step 11.2
+ err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
+ return cb(options.ignoreError ? null : err);
+ }
+ cookie.creation = oldCookie.creation; // step 11.3
+ cookie.lastAccessed = now;
+ // Step 11.4 (delete cookie) is implied by just setting the new one:
+ store.updateCookie(oldCookie, cookie, next); // step 12
+
+ } else {
+ cookie.creation = cookie.lastAccessed = now;
+ store.putCookie(cookie, next); // step 12
+ }
+ }
+
+ store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
+};
+
+// RFC6365 S5.4
+CAN_BE_SYNC.push('getCookies');
+CookieJar.prototype.getCookies = function(url, options, cb) {
+ var context = getCookieContext(url);
+ if (options instanceof Function) {
+ cb = options;
+ options = {};
+ }
+
+ var host = canonicalDomain(context.hostname);
+ var path = context.pathname || '/';
+
+ var secure = options.secure;
+ if (secure == null && context.protocol &&
+ (context.protocol == 'https:' || context.protocol == 'wss:'))
+ {
+ secure = true;
+ }
+
+ var http = options.http;
+ if (http == null) {
+ http = true;
+ }
+
+ var now = options.now || Date.now();
+ var expireCheck = options.expire !== false;
+ var allPaths = !!options.allPaths;
+ var store = this.store;
+
+ function matchingCookie(c) {
+ // "Either:
+ // The cookie's host-only-flag is true and the canonicalized
+ // request-host is identical to the cookie's domain.
+ // Or:
+ // The cookie's host-only-flag is false and the canonicalized
+ // request-host domain-matches the cookie's domain."
+ if (c.hostOnly) {
+ if (c.domain != host) {
+ return false;
+ }
+ } else {
+ if (!domainMatch(host, c.domain, false)) {
+ return false;
+ }
+ }
+
+ // "The request-uri's path path-matches the cookie's path."
+ if (!allPaths && !pathMatch(path, c.path)) {
+ return false;
+ }
+
+ // "If the cookie's secure-only-flag is true, then the request-uri's
+ // scheme must denote a "secure" protocol"
+ if (c.secure && !secure) {
+ return false;
+ }
+
+ // "If the cookie's http-only-flag is true, then exclude the cookie if the
+ // cookie-string is being generated for a "non-HTTP" API"
+ if (c.httpOnly && !http) {
+ return false;
+ }
+
+ // deferred from S5.3
+ // non-RFC: allow retention of expired cookies by choice
+ if (expireCheck && c.expiryTime() <= now) {
+ store.removeCookie(c.domain, c.path, c.key, function(){}); // result ignored
+ return false;
+ }
+
+ return true;
+ }
+
+ store.findCookies(host, allPaths ? null : path, function(err,cookies) {
+ if (err) {
+ return cb(err);
+ }
+
+ cookies = cookies.filter(matchingCookie);
+
+ // sorting of S5.4 part 2
+ if (options.sort !== false) {
+ cookies = cookies.sort(cookieCompare);
+ }
+
+ // S5.4 part 3
+ var now = new Date();
+ cookies.forEach(function(c) {
+ c.lastAccessed = now;
+ });
+ // TODO persist lastAccessed
+
+ cb(null,cookies);
+ });
+};
+
+CAN_BE_SYNC.push('getCookieString');
+CookieJar.prototype.getCookieString = function(/*..., cb*/) {
+ var args = Array.prototype.slice.call(arguments,0);
+ var cb = args.pop();
+ var next = function(err,cookies) {
+ if (err) {
+ cb(err);
+ } else {
+ cb(null, cookies
+ .sort(cookieCompare)
+ .map(function(c){
+ return c.cookieString();
+ })
+ .join('; '));
+ }
+ };
+ args.push(next);
+ this.getCookies.apply(this,args);
+};
+
+CAN_BE_SYNC.push('getSetCookieStrings');
+CookieJar.prototype.getSetCookieStrings = function(/*..., cb*/) {
+ var args = Array.prototype.slice.call(arguments,0);
+ var cb = args.pop();
+ var next = function(err,cookies) {
+ if (err) {
+ cb(err);
+ } else {
+ cb(null, cookies.map(function(c){
+ return c.toString();
+ }));
+ }
+ };
+ args.push(next);
+ this.getCookies.apply(this,args);
+};
+
+// Use a closure to provide a true imperative API for synchronous stores.
+function syncWrap(method) {
+ return function() {
+ if (!this.store.synchronous) {
+ throw new Error('CookieJar store is not synchronous; use async API instead.');
+ }
+
+ var args = Array.prototype.slice.call(arguments);
+ var syncErr, syncResult;
+ args.push(function syncCb(err, result) {
+ syncErr = err;
+ syncResult = result;
+ });
+ this[method].apply(this, args);
+
+ if (syncErr) {
+ throw syncErr;
+ }
+ return syncResult;
+ };
+}
+
+// wrap all declared CAN_BE_SYNC methods in the sync wrapper
+CAN_BE_SYNC.forEach(function(method) {
+ CookieJar.prototype[method+'Sync'] = syncWrap(method);
+});
+
+module.exports = {
+ CookieJar: CookieJar,
+ Cookie: Cookie,
+ Store: Store,
+ parseDate: parseDate,
+ formatDate: formatDate,
+ parse: parse,
+ fromJSON: fromJSON,
+ domainMatch: domainMatch,
+ defaultPath: defaultPath,
+ pathMatch: pathMatch,
+ getPublicSuffix: pubsuffix.getPublicSuffix,
+ cookieCompare: cookieCompare,
+ permuteDomain: permuteDomain,
+ permutePath: permutePath,
+ canonicalDomain: canonicalDomain
+};
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+var tough = require('./cookie');
+var Store = require('./store').Store;
+var permuteDomain = tough.permuteDomain;
+var pathMatch = tough.pathMatch;
+var util = require('util');
+
+function MemoryCookieStore() {
+ Store.call(this);
+ this.idx = {};
+}
+util.inherits(MemoryCookieStore, Store);
+exports.MemoryCookieStore = MemoryCookieStore;
+MemoryCookieStore.prototype.idx = null;
+MemoryCookieStore.prototype.synchronous = true;
+
+// force a default depth:
+MemoryCookieStore.prototype.inspect = function() {
+ return "{ idx: "+util.inspect(this.idx, false, 2)+' }';
+};
+
+MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) {
+ if (!this.idx[domain]) {
+ return cb(null,undefined);
+ }
+ if (!this.idx[domain][path]) {
+ return cb(null,undefined);
+ }
+ return cb(null,this.idx[domain][path][key]||null);
+};
+
+MemoryCookieStore.prototype.findCookies = function(domain, path, cb) {
+ var results = [];
+ if (!domain) {
+ return cb(null,[]);
+ }
+
+ var pathMatcher;
+ if (!path) {
+ // null means "all paths"
+ pathMatcher = function matchAll(domainIndex) {
+ for (var curPath in domainIndex) {
+ var pathIndex = domainIndex[curPath];
+ for (var key in pathIndex) {
+ results.push(pathIndex[key]);
+ }
+ }
+ };
+
+ } else {
+ pathMatcher = function matchRFC(domainIndex) {
+ //NOTE: we should use path-match algorithm from S5.1.4 here
+ //(see : https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/canonical_cookie.cc#L299)
+ Object.keys(domainIndex).forEach(function (cookiePath) {
+ if (pathMatch(path, cookiePath)) {
+ var pathIndex = domainIndex[cookiePath];
+
+ for (var key in pathIndex) {
+ results.push(pathIndex[key]);
+ }
+ }
+ });
+ };
+ }
+
+ var domains = permuteDomain(domain) || [domain];
+ var idx = this.idx;
+ domains.forEach(function(curDomain) {
+ var domainIndex = idx[curDomain];
+ if (!domainIndex) {
+ return;
+ }
+ pathMatcher(domainIndex);
+ });
+
+ cb(null,results);
+};
+
+MemoryCookieStore.prototype.putCookie = function(cookie, cb) {
+ if (!this.idx[cookie.domain]) {
+ this.idx[cookie.domain] = {};
+ }
+ if (!this.idx[cookie.domain][cookie.path]) {
+ this.idx[cookie.domain][cookie.path] = {};
+ }
+ this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
+ cb(null);
+};
+
+MemoryCookieStore.prototype.updateCookie = function updateCookie(oldCookie, newCookie, cb) {
+ // updateCookie() may avoid updating cookies that are identical. For example,
+ // lastAccessed may not be important to some stores and an equality
+ // comparison could exclude that field.
+ this.putCookie(newCookie,cb);
+};
+
+MemoryCookieStore.prototype.removeCookie = function removeCookie(domain, path, key, cb) {
+ if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
+ delete this.idx[domain][path][key];
+ }
+ cb(null);
+};
+
+MemoryCookieStore.prototype.removeCookies = function removeCookies(domain, path, cb) {
+ if (this.idx[domain]) {
+ if (path) {
+ delete this.idx[domain][path];
+ } else {
+ delete this.idx[domain];
+ }
+ }
+ return cb(null);
+};
--- /dev/null
+/****************************************************
+ * AUTOMATICALLY GENERATED by generate-pubsuffix.js *
+ * DO NOT EDIT! *
+ ****************************************************/
+
+"use strict";
+
+var punycode = require('punycode');
+
+module.exports.getPublicSuffix = function getPublicSuffix(domain) {
+ /*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+ if (!domain) {
+ return null;
+ }
+ if (domain.match(/^\./)) {
+ return null;
+ }
+ var asciiDomain = punycode.toASCII(domain);
+ var converted = false;
+ if (asciiDomain !== domain) {
+ domain = asciiDomain;
+ converted = true;
+ }
+ if (index[domain]) {
+ return null;
+ }
+
+ domain = domain.toLowerCase();
+ var parts = domain.split('.').reverse();
+
+ var suffix = '';
+ var suffixLen = 0;
+ for (var i=0; i<parts.length; i++) {
+ var part = parts[i];
+ var starstr = '*'+suffix;
+ var partstr = part+suffix;
+
+ if (index[starstr]) { // star rule matches
+ suffixLen = i+1;
+ if (index[partstr] === false) { // exception rule matches (NB: false, not undefined)
+ suffixLen--;
+ }
+ } else if (index[partstr]) { // exact match, not exception
+ suffixLen = i+1;
+ }
+
+ suffix = '.'+partstr;
+ }
+
+ if (index['*'+suffix]) { // *.domain exists (e.g. *.kyoto.jp for domain='kyoto.jp');
+ return null;
+ }
+
+ suffixLen = suffixLen || 1;
+ if (parts.length > suffixLen) {
+ var publicSuffix = parts.slice(0,suffixLen+1).reverse().join('.');
+ return converted ? punycode.toUnicode(publicSuffix) : publicSuffix;
+ }
+
+ return null;
+};
+
+// The following generated structure is used under the MPL version 1.1
+// See public-suffix.txt for more information
+
+var index = module.exports.index = Object.freeze(
+{"ac":true,"com.ac":true,"edu.ac":true,"gov.ac":true,"net.ac":true,"mil.ac":true,"org.ac":true,"ad":true,"nom.ad":true,"ae":true,"co.ae":true,"net.ae":true,"org.ae":true,"sch.ae":true,"ac.ae":true,"gov.ae":true,"mil.ae":true,"aero":true,"accident-investigation.aero":true,"accident-prevention.aero":true,"aerobatic.aero":true,"aeroclub.aero":true,"aerodrome.aero":true,"agents.aero":true,"aircraft.aero":true,"airline.aero":true,"airport.aero":true,"air-surveillance.aero":true,"airtraffic.aero":true,"air-traffic-control.aero":true,"ambulance.aero":true,"amusement.aero":true,"association.aero":true,"author.aero":true,"ballooning.aero":true,"broker.aero":true,"caa.aero":true,"cargo.aero":true,"catering.aero":true,"certification.aero":true,"championship.aero":true,"charter.aero":true,"civilaviation.aero":true,"club.aero":true,"conference.aero":true,"consultant.aero":true,"consulting.aero":true,"control.aero":true,"council.aero":true,"crew.aero":true,"design.aero":true,"dgca.aero":true,"educator.aero":true,"emergency.aero":true,"engine.aero":true,"engineer.aero":true,"entertainment.aero":true,"equipment.aero":true,"exchange.aero":true,"express.aero":true,"federation.aero":true,"flight.aero":true,"freight.aero":true,"fuel.aero":true,"gliding.aero":true,"government.aero":true,"groundhandling.aero":true,"group.aero":true,"hanggliding.aero":true,"homebuilt.aero":true,"insurance.aero":true,"journal.aero":true,"journalist.aero":true,"leasing.aero":true,"logistics.aero":true,"magazine.aero":true,"maintenance.aero":true,"marketplace.aero":true,"media.aero":true,"microlight.aero":true,"modelling.aero":true,"navigation.aero":true,"parachuting.aero":true,"paragliding.aero":true,"passenger-association.aero":true,"pilot.aero":true,"press.aero":true,"production.aero":true,"recreation.aero":true,"repbody.aero":true,"res.aero":true,"research.aero":true,"rotorcraft.aero":true,"safety.aero":true,"scientist.aero":true,"services.aero":true,"show.aero":true,"skydiving.aero":true,"software.aero":true,"student.aero":true,"taxi.aero":true,"trader.aero":true,"trading.aero":true,"trainer.aero":true,"union.aero":true,"workinggroup.aero":true,"works.aero":true,"af":true,"gov.af":true,"com.af":true,"org.af":true,"net.af":true,"edu.af":true,"ag":true,"com.ag":true,"org.ag":true,"net.ag":true,"co.ag":true,"nom.ag":true,"ai":true,"off.ai":true,"com.ai":true,"net.ai":true,"org.ai":true,"al":true,"com.al":true,"edu.al":true,"gov.al":true,"mil.al":true,"net.al":true,"org.al":true,"am":true,"an":true,"com.an":true,"net.an":true,"org.an":true,"edu.an":true,"ao":true,"ed.ao":true,"gv.ao":true,"og.ao":true,"co.ao":true,"pb.ao":true,"it.ao":true,"aq":true,"ar":true,"com.ar":true,"edu.ar":true,"gob.ar":true,"gov.ar":true,"int.ar":true,"mil.ar":true,"net.ar":true,"org.ar":true,"tur.ar":true,"arpa":true,"e164.arpa":true,"in-addr.arpa":true,"ip6.arpa":true,"iris.arpa":true,"uri.arpa":true,"urn.arpa":true,"as":true,"gov.as":true,"asia":true,"at":true,"ac.at":true,"co.at":true,"gv.at":true,"or.at":true,"au":true,"com.au":true,"net.au":true,"org.au":true,"edu.au":true,"gov.au":true,"asn.au":true,"id.au":true,"info.au":true,"conf.au":true,"oz.au":true,"act.au":true,"nsw.au":true,"nt.au":true,"qld.au":true,"sa.au":true,"tas.au":true,"vic.au":true,"wa.au":true,"act.edu.au":true,"nsw.edu.au":true,"nt.edu.au":true,"qld.edu.au":true,"sa.edu.au":true,"tas.edu.au":true,"vic.edu.au":true,"wa.edu.au":true,"qld.gov.au":true,"sa.gov.au":true,"tas.gov.au":true,"vic.gov.au":true,"wa.gov.au":true,"aw":true,"com.aw":true,"ax":true,"az":true,"com.az":true,"net.az":true,"int.az":true,"gov.az":true,"org.az":true,"edu.az":true,"info.az":true,"pp.az":true,"mil.az":true,"name.az":true,"pro.az":true,"biz.az":true,"ba":true,"org.ba":true,"net.ba":true,"edu.ba":true,"gov.ba":true,"mil.ba":true,"unsa.ba":true,"unbi.ba":true,"co.ba":true,"com.ba":true,"rs.ba":true,"bb":true,"biz.bb":true,"co.bb":true,"com.bb":true,"edu.bb":true,"gov.bb":true,"info.bb":true,"net.bb":true,"org.bb":true,"store.bb":true,"tv.bb":true,"*.bd":true,"be":true,"ac.be":true,"bf":true,"gov.bf":true,"bg":true,"a.bg":true,"b.bg":true,"c.bg":true,"d.bg":true,"e.bg":true,"f.bg":true,"g.bg":true,"h.bg":true,"i.bg":true,"j.bg":true,"k.bg":true,"l.bg":true,"m.bg":true,"n.bg":true,"o.bg":true,"p.bg":true,"q.bg":true,"r.bg":true,"s.bg":true,"t.bg":true,"u.bg":true,"v.bg":true,"w.bg":true,"x.bg":true,"y.bg":true,"z.bg":true,"0.bg":true,"1.bg":true,"2.bg":true,"3.bg":true,"4.bg":true,"5.bg":true,"6.bg":true,"7.bg":true,"8.bg":true,"9.bg":true,"bh":true,"com.bh":true,"edu.bh":true,"net.bh":true,"org.bh":true,"gov.bh":true,"bi":true,"co.bi":true,"com.bi":true,"edu.bi":true,"or.bi":true,"org.bi":true,"biz":true,"bj":true,"asso.bj":true,"barreau.bj":true,"gouv.bj":true,"bm":true,"com.bm":true,"edu.bm":true,"gov.bm":true,"net.bm":true,"org.bm":true,"*.bn":true,"bo":true,"com.bo":true,"edu.bo":true,"gov.bo":true,"gob.bo":true,"int.bo":true,"org.bo":true,"net.bo":true,"mil.bo":true,"tv.bo":true,"br":true,"adm.br":true,"adv.br":true,"agr.br":true,"am.br":true,"arq.br":true,"art.br":true,"ato.br":true,"b.br":true,"bio.br":true,"blog.br":true,"bmd.br":true,"cim.br":true,"cng.br":true,"cnt.br":true,"com.br":true,"coop.br":true,"ecn.br":true,"eco.br":true,"edu.br":true,"emp.br":true,"eng.br":true,"esp.br":true,"etc.br":true,"eti.br":true,"far.br":true,"flog.br":true,"fm.br":true,"fnd.br":true,"fot.br":true,"fst.br":true,"g12.br":true,"ggf.br":true,"gov.br":true,"imb.br":true,"ind.br":true,"inf.br":true,"jor.br":true,"jus.br":true,"leg.br":true,"lel.br":true,"mat.br":true,"med.br":true,"mil.br":true,"mp.br":true,"mus.br":true,"net.br":true,"*.nom.br":true,"not.br":true,"ntr.br":true,"odo.br":true,"org.br":true,"ppg.br":true,"pro.br":true,"psc.br":true,"psi.br":true,"qsl.br":true,"radio.br":true,"rec.br":true,"slg.br":true,"srv.br":true,"taxi.br":true,"teo.br":true,"tmp.br":true,"trd.br":true,"tur.br":true,"tv.br":true,"vet.br":true,"vlog.br":true,"wiki.br":true,"zlg.br":true,"bs":true,"com.bs":true,"net.bs":true,"org.bs":true,"edu.bs":true,"gov.bs":true,"bt":true,"com.bt":true,"edu.bt":true,"gov.bt":true,"net.bt":true,"org.bt":true,"bv":true,"bw":true,"co.bw":true,"org.bw":true,"by":true,"gov.by":true,"mil.by":true,"com.by":true,"of.by":true,"bz":true,"com.bz":true,"net.bz":true,"org.bz":true,"edu.bz":true,"gov.bz":true,"ca":true,"ab.ca":true,"bc.ca":true,"mb.ca":true,"nb.ca":true,"nf.ca":true,"nl.ca":true,"ns.ca":true,"nt.ca":true,"nu.ca":true,"on.ca":true,"pe.ca":true,"qc.ca":true,"sk.ca":true,"yk.ca":true,"gc.ca":true,"cat":true,"cc":true,"cd":true,"gov.cd":true,"cf":true,"cg":true,"ch":true,"ci":true,"org.ci":true,"or.ci":true,"com.ci":true,"co.ci":true,"edu.ci":true,"ed.ci":true,"ac.ci":true,"net.ci":true,"go.ci":true,"asso.ci":true,"xn--aroport-bya.ci":true,"int.ci":true,"presse.ci":true,"md.ci":true,"gouv.ci":true,"*.ck":true,"www.ck":false,"cl":true,"gov.cl":true,"gob.cl":true,"co.cl":true,"mil.cl":true,"cm":true,"co.cm":true,"com.cm":true,"gov.cm":true,"net.cm":true,"cn":true,"ac.cn":true,"com.cn":true,"edu.cn":true,"gov.cn":true,"net.cn":true,"org.cn":true,"mil.cn":true,"xn--55qx5d.cn":true,"xn--io0a7i.cn":true,"xn--od0alg.cn":true,"ah.cn":true,"bj.cn":true,"cq.cn":true,"fj.cn":true,"gd.cn":true,"gs.cn":true,"gz.cn":true,"gx.cn":true,"ha.cn":true,"hb.cn":true,"he.cn":true,"hi.cn":true,"hl.cn":true,"hn.cn":true,"jl.cn":true,"js.cn":true,"jx.cn":true,"ln.cn":true,"nm.cn":true,"nx.cn":true,"qh.cn":true,"sc.cn":true,"sd.cn":true,"sh.cn":true,"sn.cn":true,"sx.cn":true,"tj.cn":true,"xj.cn":true,"xz.cn":true,"yn.cn":true,"zj.cn":true,"hk.cn":true,"mo.cn":true,"tw.cn":true,"co":true,"arts.co":true,"com.co":true,"edu.co":true,"firm.co":true,"gov.co":true,"info.co":true,"int.co":true,"mil.co":true,"net.co":true,"nom.co":true,"org.co":true,"rec.co":true,"web.co":true,"com":true,"coop":true,"cr":true,"ac.cr":true,"co.cr":true,"ed.cr":true,"fi.cr":true,"go.cr":true,"or.cr":true,"sa.cr":true,"cu":true,"com.cu":true,"edu.cu":true,"org.cu":true,"net.cu":true,"gov.cu":true,"inf.cu":true,"cv":true,"cw":true,"com.cw":true,"edu.cw":true,"net.cw":true,"org.cw":true,"cx":true,"gov.cx":true,"*.cy":true,"cz":true,"de":true,"dj":true,"dk":true,"dm":true,"com.dm":true,"net.dm":true,"org.dm":true,"edu.dm":true,"gov.dm":true,"do":true,"art.do":true,"com.do":true,"edu.do":true,"gob.do":true,"gov.do":true,"mil.do":true,"net.do":true,"org.do":true,"sld.do":true,"web.do":true,"dz":true,"com.dz":true,"org.dz":true,"net.dz":true,"gov.dz":true,"edu.dz":true,"asso.dz":true,"pol.dz":true,"art.dz":true,"ec":true,"com.ec":true,"info.ec":true,"net.ec":true,"fin.ec":true,"k12.ec":true,"med.ec":true,"pro.ec":true,"org.ec":true,"edu.ec":true,"gov.ec":true,"gob.ec":true,"mil.ec":true,"edu":true,"ee":true,"edu.ee":true,"gov.ee":true,"riik.ee":true,"lib.ee":true,"med.ee":true,"com.ee":true,"pri.ee":true,"aip.ee":true,"org.ee":true,"fie.ee":true,"eg":true,"com.eg":true,"edu.eg":true,"eun.eg":true,"gov.eg":true,"mil.eg":true,"name.eg":true,"net.eg":true,"org.eg":true,"sci.eg":true,"*.er":true,"es":true,"com.es":true,"nom.es":true,"org.es":true,"gob.es":true,"edu.es":true,"et":true,"com.et":true,"gov.et":true,"org.et":true,"edu.et":true,"biz.et":true,"name.et":true,"info.et":true,"eu":true,"fi":true,"aland.fi":true,"*.fj":true,"*.fk":true,"fm":true,"fo":true,"fr":true,"com.fr":true,"asso.fr":true,"nom.fr":true,"prd.fr":true,"presse.fr":true,"tm.fr":true,"aeroport.fr":true,"assedic.fr":true,"avocat.fr":true,"avoues.fr":true,"cci.fr":true,"chambagri.fr":true,"chirurgiens-dentistes.fr":true,"experts-comptables.fr":true,"geometre-expert.fr":true,"gouv.fr":true,"greta.fr":true,"huissier-justice.fr":true,"medecin.fr":true,"notaires.fr":true,"pharmacien.fr":true,"port.fr":true,"veterinaire.fr":true,"ga":true,"gb":true,"gd":true,"ge":true,"com.ge":true,"edu.ge":true,"gov.ge":true,"org.ge":true,"mil.ge":true,"net.ge":true,"pvt.ge":true,"gf":true,"gg":true,"co.gg":true,"net.gg":true,"org.gg":true,"gh":true,"com.gh":true,"edu.gh":true,"gov.gh":true,"org.gh":true,"mil.gh":true,"gi":true,"com.gi":true,"ltd.gi":true,"gov.gi":true,"mod.gi":true,"edu.gi":true,"org.gi":true,"gl":true,"gm":true,"gn":true,"ac.gn":true,"com.gn":true,"edu.gn":true,"gov.gn":true,"org.gn":true,"net.gn":true,"gov":true,"gp":true,"com.gp":true,"net.gp":true,"mobi.gp":true,"edu.gp":true,"org.gp":true,"asso.gp":true,"gq":true,"gr":true,"com.gr":true,"edu.gr":true,"net.gr":true,"org.gr":true,"gov.gr":true,"gs":true,"gt":true,"com.gt":true,"edu.gt":true,"gob.gt":true,"ind.gt":true,"mil.gt":true,"net.gt":true,"org.gt":true,"*.gu":true,"gw":true,"gy":true,"co.gy":true,"com.gy":true,"net.gy":true,"hk":true,"com.hk":true,"edu.hk":true,"gov.hk":true,"idv.hk":true,"net.hk":true,"org.hk":true,"xn--55qx5d.hk":true,"xn--wcvs22d.hk":true,"xn--lcvr32d.hk":true,"xn--mxtq1m.hk":true,"xn--gmqw5a.hk":true,"xn--ciqpn.hk":true,"xn--gmq050i.hk":true,"xn--zf0avx.hk":true,"xn--io0a7i.hk":true,"xn--mk0axi.hk":true,"xn--od0alg.hk":true,"xn--od0aq3b.hk":true,"xn--tn0ag.hk":true,"xn--uc0atv.hk":true,"xn--uc0ay4a.hk":true,"hm":true,"hn":true,"com.hn":true,"edu.hn":true,"org.hn":true,"net.hn":true,"mil.hn":true,"gob.hn":true,"hr":true,"iz.hr":true,"from.hr":true,"name.hr":true,"com.hr":true,"ht":true,"com.ht":true,"shop.ht":true,"firm.ht":true,"info.ht":true,"adult.ht":true,"net.ht":true,"pro.ht":true,"org.ht":true,"med.ht":true,"art.ht":true,"coop.ht":true,"pol.ht":true,"asso.ht":true,"edu.ht":true,"rel.ht":true,"gouv.ht":true,"perso.ht":true,"hu":true,"co.hu":true,"info.hu":true,"org.hu":true,"priv.hu":true,"sport.hu":true,"tm.hu":true,"2000.hu":true,"agrar.hu":true,"bolt.hu":true,"casino.hu":true,"city.hu":true,"erotica.hu":true,"erotika.hu":true,"film.hu":true,"forum.hu":true,"games.hu":true,"hotel.hu":true,"ingatlan.hu":true,"jogasz.hu":true,"konyvelo.hu":true,"lakas.hu":true,"media.hu":true,"news.hu":true,"reklam.hu":true,"sex.hu":true,"shop.hu":true,"suli.hu":true,"szex.hu":true,"tozsde.hu":true,"utazas.hu":true,"video.hu":true,"id":true,"ac.id":true,"biz.id":true,"co.id":true,"desa.id":true,"go.id":true,"mil.id":true,"my.id":true,"net.id":true,"or.id":true,"sch.id":true,"web.id":true,"ie":true,"gov.ie":true,"*.il":true,"im":true,"ac.im":true,"co.im":true,"com.im":true,"ltd.co.im":true,"net.im":true,"org.im":true,"plc.co.im":true,"tt.im":true,"tv.im":true,"in":true,"co.in":true,"firm.in":true,"net.in":true,"org.in":true,"gen.in":true,"ind.in":true,"nic.in":true,"ac.in":true,"edu.in":true,"res.in":true,"gov.in":true,"mil.in":true,"info":true,"int":true,"eu.int":true,"io":true,"com.io":true,"iq":true,"gov.iq":true,"edu.iq":true,"mil.iq":true,"com.iq":true,"org.iq":true,"net.iq":true,"ir":true,"ac.ir":true,"co.ir":true,"gov.ir":true,"id.ir":true,"net.ir":true,"org.ir":true,"sch.ir":true,"xn--mgba3a4f16a.ir":true,"xn--mgba3a4fra.ir":true,"is":true,"net.is":true,"com.is":true,"edu.is":true,"gov.is":true,"org.is":true,"int.is":true,"it":true,"gov.it":true,"edu.it":true,"abr.it":true,"abruzzo.it":true,"aosta-valley.it":true,"aostavalley.it":true,"bas.it":true,"basilicata.it":true,"cal.it":true,"calabria.it":true,"cam.it":true,"campania.it":true,"emilia-romagna.it":true,"emiliaromagna.it":true,"emr.it":true,"friuli-v-giulia.it":true,"friuli-ve-giulia.it":true,"friuli-vegiulia.it":true,"friuli-venezia-giulia.it":true,"friuli-veneziagiulia.it":true,"friuli-vgiulia.it":true,"friuliv-giulia.it":true,"friulive-giulia.it":true,"friulivegiulia.it":true,"friulivenezia-giulia.it":true,"friuliveneziagiulia.it":true,"friulivgiulia.it":true,"fvg.it":true,"laz.it":true,"lazio.it":true,"lig.it":true,"liguria.it":true,"lom.it":true,"lombardia.it":true,"lombardy.it":true,"lucania.it":true,"mar.it":true,"marche.it":true,"mol.it":true,"molise.it":true,"piedmont.it":true,"piemonte.it":true,"pmn.it":true,"pug.it":true,"puglia.it":true,"sar.it":true,"sardegna.it":true,"sardinia.it":true,"sic.it":true,"sicilia.it":true,"sicily.it":true,"taa.it":true,"tos.it":true,"toscana.it":true,"trentino-a-adige.it":true,"trentino-aadige.it":true,"trentino-alto-adige.it":true,"trentino-altoadige.it":true,"trentino-s-tirol.it":true,"trentino-stirol.it":true,"trentino-sud-tirol.it":true,"trentino-sudtirol.it":true,"trentino-sued-tirol.it":true,"trentino-suedtirol.it":true,"trentinoa-adige.it":true,"trentinoaadige.it":true,"trentinoalto-adige.it":true,"trentinoaltoadige.it":true,"trentinos-tirol.it":true,"trentinostirol.it":true,"trentinosud-tirol.it":true,"trentinosudtirol.it":true,"trentinosued-tirol.it":true,"trentinosuedtirol.it":true,"tuscany.it":true,"umb.it":true,"umbria.it":true,"val-d-aosta.it":true,"val-daosta.it":true,"vald-aosta.it":true,"valdaosta.it":true,"valle-aosta.it":true,"valle-d-aosta.it":true,"valle-daosta.it":true,"valleaosta.it":true,"valled-aosta.it":true,"valledaosta.it":true,"vallee-aoste.it":true,"valleeaoste.it":true,"vao.it":true,"vda.it":true,"ven.it":true,"veneto.it":true,"ag.it":true,"agrigento.it":true,"al.it":true,"alessandria.it":true,"alto-adige.it":true,"altoadige.it":true,"an.it":true,"ancona.it":true,"andria-barletta-trani.it":true,"andria-trani-barletta.it":true,"andriabarlettatrani.it":true,"andriatranibarletta.it":true,"ao.it":true,"aosta.it":true,"aoste.it":true,"ap.it":true,"aq.it":true,"aquila.it":true,"ar.it":true,"arezzo.it":true,"ascoli-piceno.it":true,"ascolipiceno.it":true,"asti.it":true,"at.it":true,"av.it":true,"avellino.it":true,"ba.it":true,"balsan.it":true,"bari.it":true,"barletta-trani-andria.it":true,"barlettatraniandria.it":true,"belluno.it":true,"benevento.it":true,"bergamo.it":true,"bg.it":true,"bi.it":true,"biella.it":true,"bl.it":true,"bn.it":true,"bo.it":true,"bologna.it":true,"bolzano.it":true,"bozen.it":true,"br.it":true,"brescia.it":true,"brindisi.it":true,"bs.it":true,"bt.it":true,"bz.it":true,"ca.it":true,"cagliari.it":true,"caltanissetta.it":true,"campidano-medio.it":true,"campidanomedio.it":true,"campobasso.it":true,"carbonia-iglesias.it":true,"carboniaiglesias.it":true,"carrara-massa.it":true,"carraramassa.it":true,"caserta.it":true,"catania.it":true,"catanzaro.it":true,"cb.it":true,"ce.it":true,"cesena-forli.it":true,"cesenaforli.it":true,"ch.it":true,"chieti.it":true,"ci.it":true,"cl.it":true,"cn.it":true,"co.it":true,"como.it":true,"cosenza.it":true,"cr.it":true,"cremona.it":true,"crotone.it":true,"cs.it":true,"ct.it":true,"cuneo.it":true,"cz.it":true,"dell-ogliastra.it":true,"dellogliastra.it":true,"en.it":true,"enna.it":true,"fc.it":true,"fe.it":true,"fermo.it":true,"ferrara.it":true,"fg.it":true,"fi.it":true,"firenze.it":true,"florence.it":true,"fm.it":true,"foggia.it":true,"forli-cesena.it":true,"forlicesena.it":true,"fr.it":true,"frosinone.it":true,"ge.it":true,"genoa.it":true,"genova.it":true,"go.it":true,"gorizia.it":true,"gr.it":true,"grosseto.it":true,"iglesias-carbonia.it":true,"iglesiascarbonia.it":true,"im.it":true,"imperia.it":true,"is.it":true,"isernia.it":true,"kr.it":true,"la-spezia.it":true,"laquila.it":true,"laspezia.it":true,"latina.it":true,"lc.it":true,"le.it":true,"lecce.it":true,"lecco.it":true,"li.it":true,"livorno.it":true,"lo.it":true,"lodi.it":true,"lt.it":true,"lu.it":true,"lucca.it":true,"macerata.it":true,"mantova.it":true,"massa-carrara.it":true,"massacarrara.it":true,"matera.it":true,"mb.it":true,"mc.it":true,"me.it":true,"medio-campidano.it":true,"mediocampidano.it":true,"messina.it":true,"mi.it":true,"milan.it":true,"milano.it":true,"mn.it":true,"mo.it":true,"modena.it":true,"monza-brianza.it":true,"monza-e-della-brianza.it":true,"monza.it":true,"monzabrianza.it":true,"monzaebrianza.it":true,"monzaedellabrianza.it":true,"ms.it":true,"mt.it":true,"na.it":true,"naples.it":true,"napoli.it":true,"no.it":true,"novara.it":true,"nu.it":true,"nuoro.it":true,"og.it":true,"ogliastra.it":true,"olbia-tempio.it":true,"olbiatempio.it":true,"or.it":true,"oristano.it":true,"ot.it":true,"pa.it":true,"padova.it":true,"padua.it":true,"palermo.it":true,"parma.it":true,"pavia.it":true,"pc.it":true,"pd.it":true,"pe.it":true,"perugia.it":true,"pesaro-urbino.it":true,"pesarourbino.it":true,"pescara.it":true,"pg.it":true,"pi.it":true,"piacenza.it":true,"pisa.it":true,"pistoia.it":true,"pn.it":true,"po.it":true,"pordenone.it":true,"potenza.it":true,"pr.it":true,"prato.it":true,"pt.it":true,"pu.it":true,"pv.it":true,"pz.it":true,"ra.it":true,"ragusa.it":true,"ravenna.it":true,"rc.it":true,"re.it":true,"reggio-calabria.it":true,"reggio-emilia.it":true,"reggiocalabria.it":true,"reggioemilia.it":true,"rg.it":true,"ri.it":true,"rieti.it":true,"rimini.it":true,"rm.it":true,"rn.it":true,"ro.it":true,"roma.it":true,"rome.it":true,"rovigo.it":true,"sa.it":true,"salerno.it":true,"sassari.it":true,"savona.it":true,"si.it":true,"siena.it":true,"siracusa.it":true,"so.it":true,"sondrio.it":true,"sp.it":true,"sr.it":true,"ss.it":true,"suedtirol.it":true,"sv.it":true,"ta.it":true,"taranto.it":true,"te.it":true,"tempio-olbia.it":true,"tempioolbia.it":true,"teramo.it":true,"terni.it":true,"tn.it":true,"to.it":true,"torino.it":true,"tp.it":true,"tr.it":true,"trani-andria-barletta.it":true,"trani-barletta-andria.it":true,"traniandriabarletta.it":true,"tranibarlettaandria.it":true,"trapani.it":true,"trentino.it":true,"trento.it":true,"treviso.it":true,"trieste.it":true,"ts.it":true,"turin.it":true,"tv.it":true,"ud.it":true,"udine.it":true,"urbino-pesaro.it":true,"urbinopesaro.it":true,"va.it":true,"varese.it":true,"vb.it":true,"vc.it":true,"ve.it":true,"venezia.it":true,"venice.it":true,"verbania.it":true,"vercelli.it":true,"verona.it":true,"vi.it":true,"vibo-valentia.it":true,"vibovalentia.it":true,"vicenza.it":true,"viterbo.it":true,"vr.it":true,"vs.it":true,"vt.it":true,"vv.it":true,"je":true,"co.je":true,"net.je":true,"org.je":true,"*.jm":true,"jo":true,"com.jo":true,"org.jo":true,"net.jo":true,"edu.jo":true,"sch.jo":true,"gov.jo":true,"mil.jo":true,"name.jo":true,"jobs":true,"jp":true,"ac.jp":true,"ad.jp":true,"co.jp":true,"ed.jp":true,"go.jp":true,"gr.jp":true,"lg.jp":true,"ne.jp":true,"or.jp":true,"aichi.jp":true,"akita.jp":true,"aomori.jp":true,"chiba.jp":true,"ehime.jp":true,"fukui.jp":true,"fukuoka.jp":true,"fukushima.jp":true,"gifu.jp":true,"gunma.jp":true,"hiroshima.jp":true,"hokkaido.jp":true,"hyogo.jp":true,"ibaraki.jp":true,"ishikawa.jp":true,"iwate.jp":true,"kagawa.jp":true,"kagoshima.jp":true,"kanagawa.jp":true,"kochi.jp":true,"kumamoto.jp":true,"kyoto.jp":true,"mie.jp":true,"miyagi.jp":true,"miyazaki.jp":true,"nagano.jp":true,"nagasaki.jp":true,"nara.jp":true,"niigata.jp":true,"oita.jp":true,"okayama.jp":true,"okinawa.jp":true,"osaka.jp":true,"saga.jp":true,"saitama.jp":true,"shiga.jp":true,"shimane.jp":true,"shizuoka.jp":true,"tochigi.jp":true,"tokushima.jp":true,"tokyo.jp":true,"tottori.jp":true,"toyama.jp":true,"wakayama.jp":true,"yamagata.jp":true,"yamaguchi.jp":true,"yamanashi.jp":true,"xn--4pvxs.jp":true,"xn--vgu402c.jp":true,"xn--c3s14m.jp":true,"xn--f6qx53a.jp":true,"xn--8pvr4u.jp":true,"xn--uist22h.jp":true,"xn--djrs72d6uy.jp":true,"xn--mkru45i.jp":true,"xn--0trq7p7nn.jp":true,"xn--8ltr62k.jp":true,"xn--2m4a15e.jp":true,"xn--efvn9s.jp":true,"xn--32vp30h.jp":true,"xn--4it797k.jp":true,"xn--1lqs71d.jp":true,"xn--5rtp49c.jp":true,"xn--5js045d.jp":true,"xn--ehqz56n.jp":true,"xn--1lqs03n.jp":true,"xn--qqqt11m.jp":true,"xn--kbrq7o.jp":true,"xn--pssu33l.jp":true,"xn--ntsq17g.jp":true,"xn--uisz3g.jp":true,"xn--6btw5a.jp":true,"xn--1ctwo.jp":true,"xn--6orx2r.jp":true,"xn--rht61e.jp":true,"xn--rht27z.jp":true,"xn--djty4k.jp":true,"xn--nit225k.jp":true,"xn--rht3d.jp":true,"xn--klty5x.jp":true,"xn--kltx9a.jp":true,"xn--kltp7d.jp":true,"xn--uuwu58a.jp":true,"xn--zbx025d.jp":true,"xn--ntso0iqx3a.jp":true,"xn--elqq16h.jp":true,"xn--4it168d.jp":true,"xn--klt787d.jp":true,"xn--rny31h.jp":true,"xn--7t0a264c.jp":true,"xn--5rtq34k.jp":true,"xn--k7yn95e.jp":true,"xn--tor131o.jp":true,"xn--d5qv7z876c.jp":true,"*.kawasaki.jp":true,"*.kitakyushu.jp":true,"*.kobe.jp":true,"*.nagoya.jp":true,"*.sapporo.jp":true,"*.sendai.jp":true,"*.yokohama.jp":true,"city.kawasaki.jp":false,"city.kitakyushu.jp":false,"city.kobe.jp":false,"city.nagoya.jp":false,"city.sapporo.jp":false,"city.sendai.jp":false,"city.yokohama.jp":false,"aisai.aichi.jp":true,"ama.aichi.jp":true,"anjo.aichi.jp":true,"asuke.aichi.jp":true,"chiryu.aichi.jp":true,"chita.aichi.jp":true,"fuso.aichi.jp":true,"gamagori.aichi.jp":true,"handa.aichi.jp":true,"hazu.aichi.jp":true,"hekinan.aichi.jp":true,"higashiura.aichi.jp":true,"ichinomiya.aichi.jp":true,"inazawa.aichi.jp":true,"inuyama.aichi.jp":true,"isshiki.aichi.jp":true,"iwakura.aichi.jp":true,"kanie.aichi.jp":true,"kariya.aichi.jp":true,"kasugai.aichi.jp":true,"kira.aichi.jp":true,"kiyosu.aichi.jp":true,"komaki.aichi.jp":true,"konan.aichi.jp":true,"kota.aichi.jp":true,"mihama.aichi.jp":true,"miyoshi.aichi.jp":true,"nishio.aichi.jp":true,"nisshin.aichi.jp":true,"obu.aichi.jp":true,"oguchi.aichi.jp":true,"oharu.aichi.jp":true,"okazaki.aichi.jp":true,"owariasahi.aichi.jp":true,"seto.aichi.jp":true,"shikatsu.aichi.jp":true,"shinshiro.aichi.jp":true,"shitara.aichi.jp":true,"tahara.aichi.jp":true,"takahama.aichi.jp":true,"tobishima.aichi.jp":true,"toei.aichi.jp":true,"togo.aichi.jp":true,"tokai.aichi.jp":true,"tokoname.aichi.jp":true,"toyoake.aichi.jp":true,"toyohashi.aichi.jp":true,"toyokawa.aichi.jp":true,"toyone.aichi.jp":true,"toyota.aichi.jp":true,"tsushima.aichi.jp":true,"yatomi.aichi.jp":true,"akita.akita.jp":true,"daisen.akita.jp":true,"fujisato.akita.jp":true,"gojome.akita.jp":true,"hachirogata.akita.jp":true,"happou.akita.jp":true,"higashinaruse.akita.jp":true,"honjo.akita.jp":true,"honjyo.akita.jp":true,"ikawa.akita.jp":true,"kamikoani.akita.jp":true,"kamioka.akita.jp":true,"katagami.akita.jp":true,"kazuno.akita.jp":true,"kitaakita.akita.jp":true,"kosaka.akita.jp":true,"kyowa.akita.jp":true,"misato.akita.jp":true,"mitane.akita.jp":true,"moriyoshi.akita.jp":true,"nikaho.akita.jp":true,"noshiro.akita.jp":true,"odate.akita.jp":true,"oga.akita.jp":true,"ogata.akita.jp":true,"semboku.akita.jp":true,"yokote.akita.jp":true,"yurihonjo.akita.jp":true,"aomori.aomori.jp":true,"gonohe.aomori.jp":true,"hachinohe.aomori.jp":true,"hashikami.aomori.jp":true,"hiranai.aomori.jp":true,"hirosaki.aomori.jp":true,"itayanagi.aomori.jp":true,"kuroishi.aomori.jp":true,"misawa.aomori.jp":true,"mutsu.aomori.jp":true,"nakadomari.aomori.jp":true,"noheji.aomori.jp":true,"oirase.aomori.jp":true,"owani.aomori.jp":true,"rokunohe.aomori.jp":true,"sannohe.aomori.jp":true,"shichinohe.aomori.jp":true,"shingo.aomori.jp":true,"takko.aomori.jp":true,"towada.aomori.jp":true,"tsugaru.aomori.jp":true,"tsuruta.aomori.jp":true,"abiko.chiba.jp":true,"asahi.chiba.jp":true,"chonan.chiba.jp":true,"chosei.chiba.jp":true,"choshi.chiba.jp":true,"chuo.chiba.jp":true,"funabashi.chiba.jp":true,"futtsu.chiba.jp":true,"hanamigawa.chiba.jp":true,"ichihara.chiba.jp":true,"ichikawa.chiba.jp":true,"ichinomiya.chiba.jp":true,"inzai.chiba.jp":true,"isumi.chiba.jp":true,"kamagaya.chiba.jp":true,"kamogawa.chiba.jp":true,"kashiwa.chiba.jp":true,"katori.chiba.jp":true,"katsuura.chiba.jp":true,"kimitsu.chiba.jp":true,"kisarazu.chiba.jp":true,"kozaki.chiba.jp":true,"kujukuri.chiba.jp":true,"kyonan.chiba.jp":true,"matsudo.chiba.jp":true,"midori.chiba.jp":true,"mihama.chiba.jp":true,"minamiboso.chiba.jp":true,"mobara.chiba.jp":true,"mutsuzawa.chiba.jp":true,"nagara.chiba.jp":true,"nagareyama.chiba.jp":true,"narashino.chiba.jp":true,"narita.chiba.jp":true,"noda.chiba.jp":true,"oamishirasato.chiba.jp":true,"omigawa.chiba.jp":true,"onjuku.chiba.jp":true,"otaki.chiba.jp":true,"sakae.chiba.jp":true,"sakura.chiba.jp":true,"shimofusa.chiba.jp":true,"shirako.chiba.jp":true,"shiroi.chiba.jp":true,"shisui.chiba.jp":true,"sodegaura.chiba.jp":true,"sosa.chiba.jp":true,"tako.chiba.jp":true,"tateyama.chiba.jp":true,"togane.chiba.jp":true,"tohnosho.chiba.jp":true,"tomisato.chiba.jp":true,"urayasu.chiba.jp":true,"yachimata.chiba.jp":true,"yachiyo.chiba.jp":true,"yokaichiba.chiba.jp":true,"yokoshibahikari.chiba.jp":true,"yotsukaido.chiba.jp":true,"ainan.ehime.jp":true,"honai.ehime.jp":true,"ikata.ehime.jp":true,"imabari.ehime.jp":true,"iyo.ehime.jp":true,"kamijima.ehime.jp":true,"kihoku.ehime.jp":true,"kumakogen.ehime.jp":true,"masaki.ehime.jp":true,"matsuno.ehime.jp":true,"matsuyama.ehime.jp":true,"namikata.ehime.jp":true,"niihama.ehime.jp":true,"ozu.ehime.jp":true,"saijo.ehime.jp":true,"seiyo.ehime.jp":true,"shikokuchuo.ehime.jp":true,"tobe.ehime.jp":true,"toon.ehime.jp":true,"uchiko.ehime.jp":true,"uwajima.ehime.jp":true,"yawatahama.ehime.jp":true,"echizen.fukui.jp":true,"eiheiji.fukui.jp":true,"fukui.fukui.jp":true,"ikeda.fukui.jp":true,"katsuyama.fukui.jp":true,"mihama.fukui.jp":true,"minamiechizen.fukui.jp":true,"obama.fukui.jp":true,"ohi.fukui.jp":true,"ono.fukui.jp":true,"sabae.fukui.jp":true,"sakai.fukui.jp":true,"takahama.fukui.jp":true,"tsuruga.fukui.jp":true,"wakasa.fukui.jp":true,"ashiya.fukuoka.jp":true,"buzen.fukuoka.jp":true,"chikugo.fukuoka.jp":true,"chikuho.fukuoka.jp":true,"chikujo.fukuoka.jp":true,"chikushino.fukuoka.jp":true,"chikuzen.fukuoka.jp":true,"chuo.fukuoka.jp":true,"dazaifu.fukuoka.jp":true,"fukuchi.fukuoka.jp":true,"hakata.fukuoka.jp":true,"higashi.fukuoka.jp":true,"hirokawa.fukuoka.jp":true,"hisayama.fukuoka.jp":true,"iizuka.fukuoka.jp":true,"inatsuki.fukuoka.jp":true,"kaho.fukuoka.jp":true,"kasuga.fukuoka.jp":true,"kasuya.fukuoka.jp":true,"kawara.fukuoka.jp":true,"keisen.fukuoka.jp":true,"koga.fukuoka.jp":true,"kurate.fukuoka.jp":true,"kurogi.fukuoka.jp":true,"kurume.fukuoka.jp":true,"minami.fukuoka.jp":true,"miyako.fukuoka.jp":true,"miyama.fukuoka.jp":true,"miyawaka.fukuoka.jp":true,"mizumaki.fukuoka.jp":true,"munakata.fukuoka.jp":true,"nakagawa.fukuoka.jp":true,"nakama.fukuoka.jp":true,"nishi.fukuoka.jp":true,"nogata.fukuoka.jp":true,"ogori.fukuoka.jp":true,"okagaki.fukuoka.jp":true,"okawa.fukuoka.jp":true,"oki.fukuoka.jp":true,"omuta.fukuoka.jp":true,"onga.fukuoka.jp":true,"onojo.fukuoka.jp":true,"oto.fukuoka.jp":true,"saigawa.fukuoka.jp":true,"sasaguri.fukuoka.jp":true,"shingu.fukuoka.jp":true,"shinyoshitomi.fukuoka.jp":true,"shonai.fukuoka.jp":true,"soeda.fukuoka.jp":true,"sue.fukuoka.jp":true,"tachiarai.fukuoka.jp":true,"tagawa.fukuoka.jp":true,"takata.fukuoka.jp":true,"toho.fukuoka.jp":true,"toyotsu.fukuoka.jp":true,"tsuiki.fukuoka.jp":true,"ukiha.fukuoka.jp":true,"umi.fukuoka.jp":true,"usui.fukuoka.jp":true,"yamada.fukuoka.jp":true,"yame.fukuoka.jp":true,"yanagawa.fukuoka.jp":true,"yukuhashi.fukuoka.jp":true,"aizubange.fukushima.jp":true,"aizumisato.fukushima.jp":true,"aizuwakamatsu.fukushima.jp":true,"asakawa.fukushima.jp":true,"bandai.fukushima.jp":true,"date.fukushima.jp":true,"fukushima.fukushima.jp":true,"furudono.fukushima.jp":true,"futaba.fukushima.jp":true,"hanawa.fukushima.jp":true,"higashi.fukushima.jp":true,"hirata.fukushima.jp":true,"hirono.fukushima.jp":true,"iitate.fukushima.jp":true,"inawashiro.fukushima.jp":true,"ishikawa.fukushima.jp":true,"iwaki.fukushima.jp":true,"izumizaki.fukushima.jp":true,"kagamiishi.fukushima.jp":true,"kaneyama.fukushima.jp":true,"kawamata.fukushima.jp":true,"kitakata.fukushima.jp":true,"kitashiobara.fukushima.jp":true,"koori.fukushima.jp":true,"koriyama.fukushima.jp":true,"kunimi.fukushima.jp":true,"miharu.fukushima.jp":true,"mishima.fukushima.jp":true,"namie.fukushima.jp":true,"nango.fukushima.jp":true,"nishiaizu.fukushima.jp":true,"nishigo.fukushima.jp":true,"okuma.fukushima.jp":true,"omotego.fukushima.jp":true,"ono.fukushima.jp":true,"otama.fukushima.jp":true,"samegawa.fukushima.jp":true,"shimogo.fukushima.jp":true,"shirakawa.fukushima.jp":true,"showa.fukushima.jp":true,"soma.fukushima.jp":true,"sukagawa.fukushima.jp":true,"taishin.fukushima.jp":true,"tamakawa.fukushima.jp":true,"tanagura.fukushima.jp":true,"tenei.fukushima.jp":true,"yabuki.fukushima.jp":true,"yamato.fukushima.jp":true,"yamatsuri.fukushima.jp":true,"yanaizu.fukushima.jp":true,"yugawa.fukushima.jp":true,"anpachi.gifu.jp":true,"ena.gifu.jp":true,"gifu.gifu.jp":true,"ginan.gifu.jp":true,"godo.gifu.jp":true,"gujo.gifu.jp":true,"hashima.gifu.jp":true,"hichiso.gifu.jp":true,"hida.gifu.jp":true,"higashishirakawa.gifu.jp":true,"ibigawa.gifu.jp":true,"ikeda.gifu.jp":true,"kakamigahara.gifu.jp":true,"kani.gifu.jp":true,"kasahara.gifu.jp":true,"kasamatsu.gifu.jp":true,"kawaue.gifu.jp":true,"kitagata.gifu.jp":true,"mino.gifu.jp":true,"minokamo.gifu.jp":true,"mitake.gifu.jp":true,"mizunami.gifu.jp":true,"motosu.gifu.jp":true,"nakatsugawa.gifu.jp":true,"ogaki.gifu.jp":true,"sakahogi.gifu.jp":true,"seki.gifu.jp":true,"sekigahara.gifu.jp":true,"shirakawa.gifu.jp":true,"tajimi.gifu.jp":true,"takayama.gifu.jp":true,"tarui.gifu.jp":true,"toki.gifu.jp":true,"tomika.gifu.jp":true,"wanouchi.gifu.jp":true,"yamagata.gifu.jp":true,"yaotsu.gifu.jp":true,"yoro.gifu.jp":true,"annaka.gunma.jp":true,"chiyoda.gunma.jp":true,"fujioka.gunma.jp":true,"higashiagatsuma.gunma.jp":true,"isesaki.gunma.jp":true,"itakura.gunma.jp":true,"kanna.gunma.jp":true,"kanra.gunma.jp":true,"katashina.gunma.jp":true,"kawaba.gunma.jp":true,"kiryu.gunma.jp":true,"kusatsu.gunma.jp":true,"maebashi.gunma.jp":true,"meiwa.gunma.jp":true,"midori.gunma.jp":true,"minakami.gunma.jp":true,"naganohara.gunma.jp":true,"nakanojo.gunma.jp":true,"nanmoku.gunma.jp":true,"numata.gunma.jp":true,"oizumi.gunma.jp":true,"ora.gunma.jp":true,"ota.gunma.jp":true,"shibukawa.gunma.jp":true,"shimonita.gunma.jp":true,"shinto.gunma.jp":true,"showa.gunma.jp":true,"takasaki.gunma.jp":true,"takayama.gunma.jp":true,"tamamura.gunma.jp":true,"tatebayashi.gunma.jp":true,"tomioka.gunma.jp":true,"tsukiyono.gunma.jp":true,"tsumagoi.gunma.jp":true,"ueno.gunma.jp":true,"yoshioka.gunma.jp":true,"asaminami.hiroshima.jp":true,"daiwa.hiroshima.jp":true,"etajima.hiroshima.jp":true,"fuchu.hiroshima.jp":true,"fukuyama.hiroshima.jp":true,"hatsukaichi.hiroshima.jp":true,"higashihiroshima.hiroshima.jp":true,"hongo.hiroshima.jp":true,"jinsekikogen.hiroshima.jp":true,"kaita.hiroshima.jp":true,"kui.hiroshima.jp":true,"kumano.hiroshima.jp":true,"kure.hiroshima.jp":true,"mihara.hiroshima.jp":true,"miyoshi.hiroshima.jp":true,"naka.hiroshima.jp":true,"onomichi.hiroshima.jp":true,"osakikamijima.hiroshima.jp":true,"otake.hiroshima.jp":true,"saka.hiroshima.jp":true,"sera.hiroshima.jp":true,"seranishi.hiroshima.jp":true,"shinichi.hiroshima.jp":true,"shobara.hiroshima.jp":true,"takehara.hiroshima.jp":true,"abashiri.hokkaido.jp":true,"abira.hokkaido.jp":true,"aibetsu.hokkaido.jp":true,"akabira.hokkaido.jp":true,"akkeshi.hokkaido.jp":true,"asahikawa.hokkaido.jp":true,"ashibetsu.hokkaido.jp":true,"ashoro.hokkaido.jp":true,"assabu.hokkaido.jp":true,"atsuma.hokkaido.jp":true,"bibai.hokkaido.jp":true,"biei.hokkaido.jp":true,"bifuka.hokkaido.jp":true,"bihoro.hokkaido.jp":true,"biratori.hokkaido.jp":true,"chippubetsu.hokkaido.jp":true,"chitose.hokkaido.jp":true,"date.hokkaido.jp":true,"ebetsu.hokkaido.jp":true,"embetsu.hokkaido.jp":true,"eniwa.hokkaido.jp":true,"erimo.hokkaido.jp":true,"esan.hokkaido.jp":true,"esashi.hokkaido.jp":true,"fukagawa.hokkaido.jp":true,"fukushima.hokkaido.jp":true,"furano.hokkaido.jp":true,"furubira.hokkaido.jp":true,"haboro.hokkaido.jp":true,"hakodate.hokkaido.jp":true,"hamatonbetsu.hokkaido.jp":true,"hidaka.hokkaido.jp":true,"higashikagura.hokkaido.jp":true,"higashikawa.hokkaido.jp":true,"hiroo.hokkaido.jp":true,"hokuryu.hokkaido.jp":true,"hokuto.hokkaido.jp":true,"honbetsu.hokkaido.jp":true,"horokanai.hokkaido.jp":true,"horonobe.hokkaido.jp":true,"ikeda.hokkaido.jp":true,"imakane.hokkaido.jp":true,"ishikari.hokkaido.jp":true,"iwamizawa.hokkaido.jp":true,"iwanai.hokkaido.jp":true,"kamifurano.hokkaido.jp":true,"kamikawa.hokkaido.jp":true,"kamishihoro.hokkaido.jp":true,"kamisunagawa.hokkaido.jp":true,"kamoenai.hokkaido.jp":true,"kayabe.hokkaido.jp":true,"kembuchi.hokkaido.jp":true,"kikonai.hokkaido.jp":true,"kimobetsu.hokkaido.jp":true,"kitahiroshima.hokkaido.jp":true,"kitami.hokkaido.jp":true,"kiyosato.hokkaido.jp":true,"koshimizu.hokkaido.jp":true,"kunneppu.hokkaido.jp":true,"kuriyama.hokkaido.jp":true,"kuromatsunai.hokkaido.jp":true,"kushiro.hokkaido.jp":true,"kutchan.hokkaido.jp":true,"kyowa.hokkaido.jp":true,"mashike.hokkaido.jp":true,"matsumae.hokkaido.jp":true,"mikasa.hokkaido.jp":true,"minamifurano.hokkaido.jp":true,"mombetsu.hokkaido.jp":true,"moseushi.hokkaido.jp":true,"mukawa.hokkaido.jp":true,"muroran.hokkaido.jp":true,"naie.hokkaido.jp":true,"nakagawa.hokkaido.jp":true,"nakasatsunai.hokkaido.jp":true,"nakatombetsu.hokkaido.jp":true,"nanae.hokkaido.jp":true,"nanporo.hokkaido.jp":true,"nayoro.hokkaido.jp":true,"nemuro.hokkaido.jp":true,"niikappu.hokkaido.jp":true,"niki.hokkaido.jp":true,"nishiokoppe.hokkaido.jp":true,"noboribetsu.hokkaido.jp":true,"numata.hokkaido.jp":true,"obihiro.hokkaido.jp":true,"obira.hokkaido.jp":true,"oketo.hokkaido.jp":true,"okoppe.hokkaido.jp":true,"otaru.hokkaido.jp":true,"otobe.hokkaido.jp":true,"otofuke.hokkaido.jp":true,"otoineppu.hokkaido.jp":true,"oumu.hokkaido.jp":true,"ozora.hokkaido.jp":true,"pippu.hokkaido.jp":true,"rankoshi.hokkaido.jp":true,"rebun.hokkaido.jp":true,"rikubetsu.hokkaido.jp":true,"rishiri.hokkaido.jp":true,"rishirifuji.hokkaido.jp":true,"saroma.hokkaido.jp":true,"sarufutsu.hokkaido.jp":true,"shakotan.hokkaido.jp":true,"shari.hokkaido.jp":true,"shibecha.hokkaido.jp":true,"shibetsu.hokkaido.jp":true,"shikabe.hokkaido.jp":true,"shikaoi.hokkaido.jp":true,"shimamaki.hokkaido.jp":true,"shimizu.hokkaido.jp":true,"shimokawa.hokkaido.jp":true,"shinshinotsu.hokkaido.jp":true,"shintoku.hokkaido.jp":true,"shiranuka.hokkaido.jp":true,"shiraoi.hokkaido.jp":true,"shiriuchi.hokkaido.jp":true,"sobetsu.hokkaido.jp":true,"sunagawa.hokkaido.jp":true,"taiki.hokkaido.jp":true,"takasu.hokkaido.jp":true,"takikawa.hokkaido.jp":true,"takinoue.hokkaido.jp":true,"teshikaga.hokkaido.jp":true,"tobetsu.hokkaido.jp":true,"tohma.hokkaido.jp":true,"tomakomai.hokkaido.jp":true,"tomari.hokkaido.jp":true,"toya.hokkaido.jp":true,"toyako.hokkaido.jp":true,"toyotomi.hokkaido.jp":true,"toyoura.hokkaido.jp":true,"tsubetsu.hokkaido.jp":true,"tsukigata.hokkaido.jp":true,"urakawa.hokkaido.jp":true,"urausu.hokkaido.jp":true,"uryu.hokkaido.jp":true,"utashinai.hokkaido.jp":true,"wakkanai.hokkaido.jp":true,"wassamu.hokkaido.jp":true,"yakumo.hokkaido.jp":true,"yoichi.hokkaido.jp":true,"aioi.hyogo.jp":true,"akashi.hyogo.jp":true,"ako.hyogo.jp":true,"amagasaki.hyogo.jp":true,"aogaki.hyogo.jp":true,"asago.hyogo.jp":true,"ashiya.hyogo.jp":true,"awaji.hyogo.jp":true,"fukusaki.hyogo.jp":true,"goshiki.hyogo.jp":true,"harima.hyogo.jp":true,"himeji.hyogo.jp":true,"ichikawa.hyogo.jp":true,"inagawa.hyogo.jp":true,"itami.hyogo.jp":true,"kakogawa.hyogo.jp":true,"kamigori.hyogo.jp":true,"kamikawa.hyogo.jp":true,"kasai.hyogo.jp":true,"kasuga.hyogo.jp":true,"kawanishi.hyogo.jp":true,"miki.hyogo.jp":true,"minamiawaji.hyogo.jp":true,"nishinomiya.hyogo.jp":true,"nishiwaki.hyogo.jp":true,"ono.hyogo.jp":true,"sanda.hyogo.jp":true,"sannan.hyogo.jp":true,"sasayama.hyogo.jp":true,"sayo.hyogo.jp":true,"shingu.hyogo.jp":true,"shinonsen.hyogo.jp":true,"shiso.hyogo.jp":true,"sumoto.hyogo.jp":true,"taishi.hyogo.jp":true,"taka.hyogo.jp":true,"takarazuka.hyogo.jp":true,"takasago.hyogo.jp":true,"takino.hyogo.jp":true,"tamba.hyogo.jp":true,"tatsuno.hyogo.jp":true,"toyooka.hyogo.jp":true,"yabu.hyogo.jp":true,"yashiro.hyogo.jp":true,"yoka.hyogo.jp":true,"yokawa.hyogo.jp":true,"ami.ibaraki.jp":true,"asahi.ibaraki.jp":true,"bando.ibaraki.jp":true,"chikusei.ibaraki.jp":true,"daigo.ibaraki.jp":true,"fujishiro.ibaraki.jp":true,"hitachi.ibaraki.jp":true,"hitachinaka.ibaraki.jp":true,"hitachiomiya.ibaraki.jp":true,"hitachiota.ibaraki.jp":true,"ibaraki.ibaraki.jp":true,"ina.ibaraki.jp":true,"inashiki.ibaraki.jp":true,"itako.ibaraki.jp":true,"iwama.ibaraki.jp":true,"joso.ibaraki.jp":true,"kamisu.ibaraki.jp":true,"kasama.ibaraki.jp":true,"kashima.ibaraki.jp":true,"kasumigaura.ibaraki.jp":true,"koga.ibaraki.jp":true,"miho.ibaraki.jp":true,"mito.ibaraki.jp":true,"moriya.ibaraki.jp":true,"naka.ibaraki.jp":true,"namegata.ibaraki.jp":true,"oarai.ibaraki.jp":true,"ogawa.ibaraki.jp":true,"omitama.ibaraki.jp":true,"ryugasaki.ibaraki.jp":true,"sakai.ibaraki.jp":true,"sakuragawa.ibaraki.jp":true,"shimodate.ibaraki.jp":true,"shimotsuma.ibaraki.jp":true,"shirosato.ibaraki.jp":true,"sowa.ibaraki.jp":true,"suifu.ibaraki.jp":true,"takahagi.ibaraki.jp":true,"tamatsukuri.ibaraki.jp":true,"tokai.ibaraki.jp":true,"tomobe.ibaraki.jp":true,"tone.ibaraki.jp":true,"toride.ibaraki.jp":true,"tsuchiura.ibaraki.jp":true,"tsukuba.ibaraki.jp":true,"uchihara.ibaraki.jp":true,"ushiku.ibaraki.jp":true,"yachiyo.ibaraki.jp":true,"yamagata.ibaraki.jp":true,"yawara.ibaraki.jp":true,"yuki.ibaraki.jp":true,"anamizu.ishikawa.jp":true,"hakui.ishikawa.jp":true,"hakusan.ishikawa.jp":true,"kaga.ishikawa.jp":true,"kahoku.ishikawa.jp":true,"kanazawa.ishikawa.jp":true,"kawakita.ishikawa.jp":true,"komatsu.ishikawa.jp":true,"nakanoto.ishikawa.jp":true,"nanao.ishikawa.jp":true,"nomi.ishikawa.jp":true,"nonoichi.ishikawa.jp":true,"noto.ishikawa.jp":true,"shika.ishikawa.jp":true,"suzu.ishikawa.jp":true,"tsubata.ishikawa.jp":true,"tsurugi.ishikawa.jp":true,"uchinada.ishikawa.jp":true,"wajima.ishikawa.jp":true,"fudai.iwate.jp":true,"fujisawa.iwate.jp":true,"hanamaki.iwate.jp":true,"hiraizumi.iwate.jp":true,"hirono.iwate.jp":true,"ichinohe.iwate.jp":true,"ichinoseki.iwate.jp":true,"iwaizumi.iwate.jp":true,"iwate.iwate.jp":true,"joboji.iwate.jp":true,"kamaishi.iwate.jp":true,"kanegasaki.iwate.jp":true,"karumai.iwate.jp":true,"kawai.iwate.jp":true,"kitakami.iwate.jp":true,"kuji.iwate.jp":true,"kunohe.iwate.jp":true,"kuzumaki.iwate.jp":true,"miyako.iwate.jp":true,"mizusawa.iwate.jp":true,"morioka.iwate.jp":true,"ninohe.iwate.jp":true,"noda.iwate.jp":true,"ofunato.iwate.jp":true,"oshu.iwate.jp":true,"otsuchi.iwate.jp":true,"rikuzentakata.iwate.jp":true,"shiwa.iwate.jp":true,"shizukuishi.iwate.jp":true,"sumita.iwate.jp":true,"tanohata.iwate.jp":true,"tono.iwate.jp":true,"yahaba.iwate.jp":true,"yamada.iwate.jp":true,"ayagawa.kagawa.jp":true,"higashikagawa.kagawa.jp":true,"kanonji.kagawa.jp":true,"kotohira.kagawa.jp":true,"manno.kagawa.jp":true,"marugame.kagawa.jp":true,"mitoyo.kagawa.jp":true,"naoshima.kagawa.jp":true,"sanuki.kagawa.jp":true,"tadotsu.kagawa.jp":true,"takamatsu.kagawa.jp":true,"tonosho.kagawa.jp":true,"uchinomi.kagawa.jp":true,"utazu.kagawa.jp":true,"zentsuji.kagawa.jp":true,"akune.kagoshima.jp":true,"amami.kagoshima.jp":true,"hioki.kagoshima.jp":true,"isa.kagoshima.jp":true,"isen.kagoshima.jp":true,"izumi.kagoshima.jp":true,"kagoshima.kagoshima.jp":true,"kanoya.kagoshima.jp":true,"kawanabe.kagoshima.jp":true,"kinko.kagoshima.jp":true,"kouyama.kagoshima.jp":true,"makurazaki.kagoshima.jp":true,"matsumoto.kagoshima.jp":true,"minamitane.kagoshima.jp":true,"nakatane.kagoshima.jp":true,"nishinoomote.kagoshima.jp":true,"satsumasendai.kagoshima.jp":true,"soo.kagoshima.jp":true,"tarumizu.kagoshima.jp":true,"yusui.kagoshima.jp":true,"aikawa.kanagawa.jp":true,"atsugi.kanagawa.jp":true,"ayase.kanagawa.jp":true,"chigasaki.kanagawa.jp":true,"ebina.kanagawa.jp":true,"fujisawa.kanagawa.jp":true,"hadano.kanagawa.jp":true,"hakone.kanagawa.jp":true,"hiratsuka.kanagawa.jp":true,"isehara.kanagawa.jp":true,"kaisei.kanagawa.jp":true,"kamakura.kanagawa.jp":true,"kiyokawa.kanagawa.jp":true,"matsuda.kanagawa.jp":true,"minamiashigara.kanagawa.jp":true,"miura.kanagawa.jp":true,"nakai.kanagawa.jp":true,"ninomiya.kanagawa.jp":true,"odawara.kanagawa.jp":true,"oi.kanagawa.jp":true,"oiso.kanagawa.jp":true,"sagamihara.kanagawa.jp":true,"samukawa.kanagawa.jp":true,"tsukui.kanagawa.jp":true,"yamakita.kanagawa.jp":true,"yamato.kanagawa.jp":true,"yokosuka.kanagawa.jp":true,"yugawara.kanagawa.jp":true,"zama.kanagawa.jp":true,"zushi.kanagawa.jp":true,"aki.kochi.jp":true,"geisei.kochi.jp":true,"hidaka.kochi.jp":true,"higashitsuno.kochi.jp":true,"ino.kochi.jp":true,"kagami.kochi.jp":true,"kami.kochi.jp":true,"kitagawa.kochi.jp":true,"kochi.kochi.jp":true,"mihara.kochi.jp":true,"motoyama.kochi.jp":true,"muroto.kochi.jp":true,"nahari.kochi.jp":true,"nakamura.kochi.jp":true,"nankoku.kochi.jp":true,"nishitosa.kochi.jp":true,"niyodogawa.kochi.jp":true,"ochi.kochi.jp":true,"okawa.kochi.jp":true,"otoyo.kochi.jp":true,"otsuki.kochi.jp":true,"sakawa.kochi.jp":true,"sukumo.kochi.jp":true,"susaki.kochi.jp":true,"tosa.kochi.jp":true,"tosashimizu.kochi.jp":true,"toyo.kochi.jp":true,"tsuno.kochi.jp":true,"umaji.kochi.jp":true,"yasuda.kochi.jp":true,"yusuhara.kochi.jp":true,"amakusa.kumamoto.jp":true,"arao.kumamoto.jp":true,"aso.kumamoto.jp":true,"choyo.kumamoto.jp":true,"gyokuto.kumamoto.jp":true,"hitoyoshi.kumamoto.jp":true,"kamiamakusa.kumamoto.jp":true,"kashima.kumamoto.jp":true,"kikuchi.kumamoto.jp":true,"kosa.kumamoto.jp":true,"kumamoto.kumamoto.jp":true,"mashiki.kumamoto.jp":true,"mifune.kumamoto.jp":true,"minamata.kumamoto.jp":true,"minamioguni.kumamoto.jp":true,"nagasu.kumamoto.jp":true,"nishihara.kumamoto.jp":true,"oguni.kumamoto.jp":true,"ozu.kumamoto.jp":true,"sumoto.kumamoto.jp":true,"takamori.kumamoto.jp":true,"uki.kumamoto.jp":true,"uto.kumamoto.jp":true,"yamaga.kumamoto.jp":true,"yamato.kumamoto.jp":true,"yatsushiro.kumamoto.jp":true,"ayabe.kyoto.jp":true,"fukuchiyama.kyoto.jp":true,"higashiyama.kyoto.jp":true,"ide.kyoto.jp":true,"ine.kyoto.jp":true,"joyo.kyoto.jp":true,"kameoka.kyoto.jp":true,"kamo.kyoto.jp":true,"kita.kyoto.jp":true,"kizu.kyoto.jp":true,"kumiyama.kyoto.jp":true,"kyotamba.kyoto.jp":true,"kyotanabe.kyoto.jp":true,"kyotango.kyoto.jp":true,"maizuru.kyoto.jp":true,"minami.kyoto.jp":true,"minamiyamashiro.kyoto.jp":true,"miyazu.kyoto.jp":true,"muko.kyoto.jp":true,"nagaokakyo.kyoto.jp":true,"nakagyo.kyoto.jp":true,"nantan.kyoto.jp":true,"oyamazaki.kyoto.jp":true,"sakyo.kyoto.jp":true,"seika.kyoto.jp":true,"tanabe.kyoto.jp":true,"uji.kyoto.jp":true,"ujitawara.kyoto.jp":true,"wazuka.kyoto.jp":true,"yamashina.kyoto.jp":true,"yawata.kyoto.jp":true,"asahi.mie.jp":true,"inabe.mie.jp":true,"ise.mie.jp":true,"kameyama.mie.jp":true,"kawagoe.mie.jp":true,"kiho.mie.jp":true,"kisosaki.mie.jp":true,"kiwa.mie.jp":true,"komono.mie.jp":true,"kumano.mie.jp":true,"kuwana.mie.jp":true,"matsusaka.mie.jp":true,"meiwa.mie.jp":true,"mihama.mie.jp":true,"minamiise.mie.jp":true,"misugi.mie.jp":true,"miyama.mie.jp":true,"nabari.mie.jp":true,"shima.mie.jp":true,"suzuka.mie.jp":true,"tado.mie.jp":true,"taiki.mie.jp":true,"taki.mie.jp":true,"tamaki.mie.jp":true,"toba.mie.jp":true,"tsu.mie.jp":true,"udono.mie.jp":true,"ureshino.mie.jp":true,"watarai.mie.jp":true,"yokkaichi.mie.jp":true,"furukawa.miyagi.jp":true,"higashimatsushima.miyagi.jp":true,"ishinomaki.miyagi.jp":true,"iwanuma.miyagi.jp":true,"kakuda.miyagi.jp":true,"kami.miyagi.jp":true,"kawasaki.miyagi.jp":true,"kesennuma.miyagi.jp":true,"marumori.miyagi.jp":true,"matsushima.miyagi.jp":true,"minamisanriku.miyagi.jp":true,"misato.miyagi.jp":true,"murata.miyagi.jp":true,"natori.miyagi.jp":true,"ogawara.miyagi.jp":true,"ohira.miyagi.jp":true,"onagawa.miyagi.jp":true,"osaki.miyagi.jp":true,"rifu.miyagi.jp":true,"semine.miyagi.jp":true,"shibata.miyagi.jp":true,"shichikashuku.miyagi.jp":true,"shikama.miyagi.jp":true,"shiogama.miyagi.jp":true,"shiroishi.miyagi.jp":true,"tagajo.miyagi.jp":true,"taiwa.miyagi.jp":true,"tome.miyagi.jp":true,"tomiya.miyagi.jp":true,"wakuya.miyagi.jp":true,"watari.miyagi.jp":true,"yamamoto.miyagi.jp":true,"zao.miyagi.jp":true,"aya.miyazaki.jp":true,"ebino.miyazaki.jp":true,"gokase.miyazaki.jp":true,"hyuga.miyazaki.jp":true,"kadogawa.miyazaki.jp":true,"kawaminami.miyazaki.jp":true,"kijo.miyazaki.jp":true,"kitagawa.miyazaki.jp":true,"kitakata.miyazaki.jp":true,"kitaura.miyazaki.jp":true,"kobayashi.miyazaki.jp":true,"kunitomi.miyazaki.jp":true,"kushima.miyazaki.jp":true,"mimata.miyazaki.jp":true,"miyakonojo.miyazaki.jp":true,"miyazaki.miyazaki.jp":true,"morotsuka.miyazaki.jp":true,"nichinan.miyazaki.jp":true,"nishimera.miyazaki.jp":true,"nobeoka.miyazaki.jp":true,"saito.miyazaki.jp":true,"shiiba.miyazaki.jp":true,"shintomi.miyazaki.jp":true,"takaharu.miyazaki.jp":true,"takanabe.miyazaki.jp":true,"takazaki.miyazaki.jp":true,"tsuno.miyazaki.jp":true,"achi.nagano.jp":true,"agematsu.nagano.jp":true,"anan.nagano.jp":true,"aoki.nagano.jp":true,"asahi.nagano.jp":true,"azumino.nagano.jp":true,"chikuhoku.nagano.jp":true,"chikuma.nagano.jp":true,"chino.nagano.jp":true,"fujimi.nagano.jp":true,"hakuba.nagano.jp":true,"hara.nagano.jp":true,"hiraya.nagano.jp":true,"iida.nagano.jp":true,"iijima.nagano.jp":true,"iiyama.nagano.jp":true,"iizuna.nagano.jp":true,"ikeda.nagano.jp":true,"ikusaka.nagano.jp":true,"ina.nagano.jp":true,"karuizawa.nagano.jp":true,"kawakami.nagano.jp":true,"kiso.nagano.jp":true,"kisofukushima.nagano.jp":true,"kitaaiki.nagano.jp":true,"komagane.nagano.jp":true,"komoro.nagano.jp":true,"matsukawa.nagano.jp":true,"matsumoto.nagano.jp":true,"miasa.nagano.jp":true,"minamiaiki.nagano.jp":true,"minamimaki.nagano.jp":true,"minamiminowa.nagano.jp":true,"minowa.nagano.jp":true,"miyada.nagano.jp":true,"miyota.nagano.jp":true,"mochizuki.nagano.jp":true,"nagano.nagano.jp":true,"nagawa.nagano.jp":true,"nagiso.nagano.jp":true,"nakagawa.nagano.jp":true,"nakano.nagano.jp":true,"nozawaonsen.nagano.jp":true,"obuse.nagano.jp":true,"ogawa.nagano.jp":true,"okaya.nagano.jp":true,"omachi.nagano.jp":true,"omi.nagano.jp":true,"ookuwa.nagano.jp":true,"ooshika.nagano.jp":true,"otaki.nagano.jp":true,"otari.nagano.jp":true,"sakae.nagano.jp":true,"sakaki.nagano.jp":true,"saku.nagano.jp":true,"sakuho.nagano.jp":true,"shimosuwa.nagano.jp":true,"shinanomachi.nagano.jp":true,"shiojiri.nagano.jp":true,"suwa.nagano.jp":true,"suzaka.nagano.jp":true,"takagi.nagano.jp":true,"takamori.nagano.jp":true,"takayama.nagano.jp":true,"tateshina.nagano.jp":true,"tatsuno.nagano.jp":true,"togakushi.nagano.jp":true,"togura.nagano.jp":true,"tomi.nagano.jp":true,"ueda.nagano.jp":true,"wada.nagano.jp":true,"yamagata.nagano.jp":true,"yamanouchi.nagano.jp":true,"yasaka.nagano.jp":true,"yasuoka.nagano.jp":true,"chijiwa.nagasaki.jp":true,"futsu.nagasaki.jp":true,"goto.nagasaki.jp":true,"hasami.nagasaki.jp":true,"hirado.nagasaki.jp":true,"iki.nagasaki.jp":true,"isahaya.nagasaki.jp":true,"kawatana.nagasaki.jp":true,"kuchinotsu.nagasaki.jp":true,"matsuura.nagasaki.jp":true,"nagasaki.nagasaki.jp":true,"obama.nagasaki.jp":true,"omura.nagasaki.jp":true,"oseto.nagasaki.jp":true,"saikai.nagasaki.jp":true,"sasebo.nagasaki.jp":true,"seihi.nagasaki.jp":true,"shimabara.nagasaki.jp":true,"shinkamigoto.nagasaki.jp":true,"togitsu.nagasaki.jp":true,"tsushima.nagasaki.jp":true,"unzen.nagasaki.jp":true,"ando.nara.jp":true,"gose.nara.jp":true,"heguri.nara.jp":true,"higashiyoshino.nara.jp":true,"ikaruga.nara.jp":true,"ikoma.nara.jp":true,"kamikitayama.nara.jp":true,"kanmaki.nara.jp":true,"kashiba.nara.jp":true,"kashihara.nara.jp":true,"katsuragi.nara.jp":true,"kawai.nara.jp":true,"kawakami.nara.jp":true,"kawanishi.nara.jp":true,"koryo.nara.jp":true,"kurotaki.nara.jp":true,"mitsue.nara.jp":true,"miyake.nara.jp":true,"nara.nara.jp":true,"nosegawa.nara.jp":true,"oji.nara.jp":true,"ouda.nara.jp":true,"oyodo.nara.jp":true,"sakurai.nara.jp":true,"sango.nara.jp":true,"shimoichi.nara.jp":true,"shimokitayama.nara.jp":true,"shinjo.nara.jp":true,"soni.nara.jp":true,"takatori.nara.jp":true,"tawaramoto.nara.jp":true,"tenkawa.nara.jp":true,"tenri.nara.jp":true,"uda.nara.jp":true,"yamatokoriyama.nara.jp":true,"yamatotakada.nara.jp":true,"yamazoe.nara.jp":true,"yoshino.nara.jp":true,"aga.niigata.jp":true,"agano.niigata.jp":true,"gosen.niigata.jp":true,"itoigawa.niigata.jp":true,"izumozaki.niigata.jp":true,"joetsu.niigata.jp":true,"kamo.niigata.jp":true,"kariwa.niigata.jp":true,"kashiwazaki.niigata.jp":true,"minamiuonuma.niigata.jp":true,"mitsuke.niigata.jp":true,"muika.niigata.jp":true,"murakami.niigata.jp":true,"myoko.niigata.jp":true,"nagaoka.niigata.jp":true,"niigata.niigata.jp":true,"ojiya.niigata.jp":true,"omi.niigata.jp":true,"sado.niigata.jp":true,"sanjo.niigata.jp":true,"seiro.niigata.jp":true,"seirou.niigata.jp":true,"sekikawa.niigata.jp":true,"shibata.niigata.jp":true,"tagami.niigata.jp":true,"tainai.niigata.jp":true,"tochio.niigata.jp":true,"tokamachi.niigata.jp":true,"tsubame.niigata.jp":true,"tsunan.niigata.jp":true,"uonuma.niigata.jp":true,"yahiko.niigata.jp":true,"yoita.niigata.jp":true,"yuzawa.niigata.jp":true,"beppu.oita.jp":true,"bungoono.oita.jp":true,"bungotakada.oita.jp":true,"hasama.oita.jp":true,"hiji.oita.jp":true,"himeshima.oita.jp":true,"hita.oita.jp":true,"kamitsue.oita.jp":true,"kokonoe.oita.jp":true,"kuju.oita.jp":true,"kunisaki.oita.jp":true,"kusu.oita.jp":true,"oita.oita.jp":true,"saiki.oita.jp":true,"taketa.oita.jp":true,"tsukumi.oita.jp":true,"usa.oita.jp":true,"usuki.oita.jp":true,"yufu.oita.jp":true,"akaiwa.okayama.jp":true,"asakuchi.okayama.jp":true,"bizen.okayama.jp":true,"hayashima.okayama.jp":true,"ibara.okayama.jp":true,"kagamino.okayama.jp":true,"kasaoka.okayama.jp":true,"kibichuo.okayama.jp":true,"kumenan.okayama.jp":true,"kurashiki.okayama.jp":true,"maniwa.okayama.jp":true,"misaki.okayama.jp":true,"nagi.okayama.jp":true,"niimi.okayama.jp":true,"nishiawakura.okayama.jp":true,"okayama.okayama.jp":true,"satosho.okayama.jp":true,"setouchi.okayama.jp":true,"shinjo.okayama.jp":true,"shoo.okayama.jp":true,"soja.okayama.jp":true,"takahashi.okayama.jp":true,"tamano.okayama.jp":true,"tsuyama.okayama.jp":true,"wake.okayama.jp":true,"yakage.okayama.jp":true,"aguni.okinawa.jp":true,"ginowan.okinawa.jp":true,"ginoza.okinawa.jp":true,"gushikami.okinawa.jp":true,"haebaru.okinawa.jp":true,"higashi.okinawa.jp":true,"hirara.okinawa.jp":true,"iheya.okinawa.jp":true,"ishigaki.okinawa.jp":true,"ishikawa.okinawa.jp":true,"itoman.okinawa.jp":true,"izena.okinawa.jp":true,"kadena.okinawa.jp":true,"kin.okinawa.jp":true,"kitadaito.okinawa.jp":true,"kitanakagusuku.okinawa.jp":true,"kumejima.okinawa.jp":true,"kunigami.okinawa.jp":true,"minamidaito.okinawa.jp":true,"motobu.okinawa.jp":true,"nago.okinawa.jp":true,"naha.okinawa.jp":true,"nakagusuku.okinawa.jp":true,"nakijin.okinawa.jp":true,"nanjo.okinawa.jp":true,"nishihara.okinawa.jp":true,"ogimi.okinawa.jp":true,"okinawa.okinawa.jp":true,"onna.okinawa.jp":true,"shimoji.okinawa.jp":true,"taketomi.okinawa.jp":true,"tarama.okinawa.jp":true,"tokashiki.okinawa.jp":true,"tomigusuku.okinawa.jp":true,"tonaki.okinawa.jp":true,"urasoe.okinawa.jp":true,"uruma.okinawa.jp":true,"yaese.okinawa.jp":true,"yomitan.okinawa.jp":true,"yonabaru.okinawa.jp":true,"yonaguni.okinawa.jp":true,"zamami.okinawa.jp":true,"abeno.osaka.jp":true,"chihayaakasaka.osaka.jp":true,"chuo.osaka.jp":true,"daito.osaka.jp":true,"fujiidera.osaka.jp":true,"habikino.osaka.jp":true,"hannan.osaka.jp":true,"higashiosaka.osaka.jp":true,"higashisumiyoshi.osaka.jp":true,"higashiyodogawa.osaka.jp":true,"hirakata.osaka.jp":true,"ibaraki.osaka.jp":true,"ikeda.osaka.jp":true,"izumi.osaka.jp":true,"izumiotsu.osaka.jp":true,"izumisano.osaka.jp":true,"kadoma.osaka.jp":true,"kaizuka.osaka.jp":true,"kanan.osaka.jp":true,"kashiwara.osaka.jp":true,"katano.osaka.jp":true,"kawachinagano.osaka.jp":true,"kishiwada.osaka.jp":true,"kita.osaka.jp":true,"kumatori.osaka.jp":true,"matsubara.osaka.jp":true,"minato.osaka.jp":true,"minoh.osaka.jp":true,"misaki.osaka.jp":true,"moriguchi.osaka.jp":true,"neyagawa.osaka.jp":true,"nishi.osaka.jp":true,"nose.osaka.jp":true,"osakasayama.osaka.jp":true,"sakai.osaka.jp":true,"sayama.osaka.jp":true,"sennan.osaka.jp":true,"settsu.osaka.jp":true,"shijonawate.osaka.jp":true,"shimamoto.osaka.jp":true,"suita.osaka.jp":true,"tadaoka.osaka.jp":true,"taishi.osaka.jp":true,"tajiri.osaka.jp":true,"takaishi.osaka.jp":true,"takatsuki.osaka.jp":true,"tondabayashi.osaka.jp":true,"toyonaka.osaka.jp":true,"toyono.osaka.jp":true,"yao.osaka.jp":true,"ariake.saga.jp":true,"arita.saga.jp":true,"fukudomi.saga.jp":true,"genkai.saga.jp":true,"hamatama.saga.jp":true,"hizen.saga.jp":true,"imari.saga.jp":true,"kamimine.saga.jp":true,"kanzaki.saga.jp":true,"karatsu.saga.jp":true,"kashima.saga.jp":true,"kitagata.saga.jp":true,"kitahata.saga.jp":true,"kiyama.saga.jp":true,"kouhoku.saga.jp":true,"kyuragi.saga.jp":true,"nishiarita.saga.jp":true,"ogi.saga.jp":true,"omachi.saga.jp":true,"ouchi.saga.jp":true,"saga.saga.jp":true,"shiroishi.saga.jp":true,"taku.saga.jp":true,"tara.saga.jp":true,"tosu.saga.jp":true,"yoshinogari.saga.jp":true,"arakawa.saitama.jp":true,"asaka.saitama.jp":true,"chichibu.saitama.jp":true,"fujimi.saitama.jp":true,"fujimino.saitama.jp":true,"fukaya.saitama.jp":true,"hanno.saitama.jp":true,"hanyu.saitama.jp":true,"hasuda.saitama.jp":true,"hatogaya.saitama.jp":true,"hatoyama.saitama.jp":true,"hidaka.saitama.jp":true,"higashichichibu.saitama.jp":true,"higashimatsuyama.saitama.jp":true,"honjo.saitama.jp":true,"ina.saitama.jp":true,"iruma.saitama.jp":true,"iwatsuki.saitama.jp":true,"kamiizumi.saitama.jp":true,"kamikawa.saitama.jp":true,"kamisato.saitama.jp":true,"kasukabe.saitama.jp":true,"kawagoe.saitama.jp":true,"kawaguchi.saitama.jp":true,"kawajima.saitama.jp":true,"kazo.saitama.jp":true,"kitamoto.saitama.jp":true,"koshigaya.saitama.jp":true,"kounosu.saitama.jp":true,"kuki.saitama.jp":true,"kumagaya.saitama.jp":true,"matsubushi.saitama.jp":true,"minano.saitama.jp":true,"misato.saitama.jp":true,"miyashiro.saitama.jp":true,"miyoshi.saitama.jp":true,"moroyama.saitama.jp":true,"nagatoro.saitama.jp":true,"namegawa.saitama.jp":true,"niiza.saitama.jp":true,"ogano.saitama.jp":true,"ogawa.saitama.jp":true,"ogose.saitama.jp":true,"okegawa.saitama.jp":true,"omiya.saitama.jp":true,"otaki.saitama.jp":true,"ranzan.saitama.jp":true,"ryokami.saitama.jp":true,"saitama.saitama.jp":true,"sakado.saitama.jp":true,"satte.saitama.jp":true,"sayama.saitama.jp":true,"shiki.saitama.jp":true,"shiraoka.saitama.jp":true,"soka.saitama.jp":true,"sugito.saitama.jp":true,"toda.saitama.jp":true,"tokigawa.saitama.jp":true,"tokorozawa.saitama.jp":true,"tsurugashima.saitama.jp":true,"urawa.saitama.jp":true,"warabi.saitama.jp":true,"yashio.saitama.jp":true,"yokoze.saitama.jp":true,"yono.saitama.jp":true,"yorii.saitama.jp":true,"yoshida.saitama.jp":true,"yoshikawa.saitama.jp":true,"yoshimi.saitama.jp":true,"aisho.shiga.jp":true,"gamo.shiga.jp":true,"higashiomi.shiga.jp":true,"hikone.shiga.jp":true,"koka.shiga.jp":true,"konan.shiga.jp":true,"kosei.shiga.jp":true,"koto.shiga.jp":true,"kusatsu.shiga.jp":true,"maibara.shiga.jp":true,"moriyama.shiga.jp":true,"nagahama.shiga.jp":true,"nishiazai.shiga.jp":true,"notogawa.shiga.jp":true,"omihachiman.shiga.jp":true,"otsu.shiga.jp":true,"ritto.shiga.jp":true,"ryuoh.shiga.jp":true,"takashima.shiga.jp":true,"takatsuki.shiga.jp":true,"torahime.shiga.jp":true,"toyosato.shiga.jp":true,"yasu.shiga.jp":true,"akagi.shimane.jp":true,"ama.shimane.jp":true,"gotsu.shimane.jp":true,"hamada.shimane.jp":true,"higashiizumo.shimane.jp":true,"hikawa.shimane.jp":true,"hikimi.shimane.jp":true,"izumo.shimane.jp":true,"kakinoki.shimane.jp":true,"masuda.shimane.jp":true,"matsue.shimane.jp":true,"misato.shimane.jp":true,"nishinoshima.shimane.jp":true,"ohda.shimane.jp":true,"okinoshima.shimane.jp":true,"okuizumo.shimane.jp":true,"shimane.shimane.jp":true,"tamayu.shimane.jp":true,"tsuwano.shimane.jp":true,"unnan.shimane.jp":true,"yakumo.shimane.jp":true,"yasugi.shimane.jp":true,"yatsuka.shimane.jp":true,"arai.shizuoka.jp":true,"atami.shizuoka.jp":true,"fuji.shizuoka.jp":true,"fujieda.shizuoka.jp":true,"fujikawa.shizuoka.jp":true,"fujinomiya.shizuoka.jp":true,"fukuroi.shizuoka.jp":true,"gotemba.shizuoka.jp":true,"haibara.shizuoka.jp":true,"hamamatsu.shizuoka.jp":true,"higashiizu.shizuoka.jp":true,"ito.shizuoka.jp":true,"iwata.shizuoka.jp":true,"izu.shizuoka.jp":true,"izunokuni.shizuoka.jp":true,"kakegawa.shizuoka.jp":true,"kannami.shizuoka.jp":true,"kawanehon.shizuoka.jp":true,"kawazu.shizuoka.jp":true,"kikugawa.shizuoka.jp":true,"kosai.shizuoka.jp":true,"makinohara.shizuoka.jp":true,"matsuzaki.shizuoka.jp":true,"minamiizu.shizuoka.jp":true,"mishima.shizuoka.jp":true,"morimachi.shizuoka.jp":true,"nishiizu.shizuoka.jp":true,"numazu.shizuoka.jp":true,"omaezaki.shizuoka.jp":true,"shimada.shizuoka.jp":true,"shimizu.shizuoka.jp":true,"shimoda.shizuoka.jp":true,"shizuoka.shizuoka.jp":true,"susono.shizuoka.jp":true,"yaizu.shizuoka.jp":true,"yoshida.shizuoka.jp":true,"ashikaga.tochigi.jp":true,"bato.tochigi.jp":true,"haga.tochigi.jp":true,"ichikai.tochigi.jp":true,"iwafune.tochigi.jp":true,"kaminokawa.tochigi.jp":true,"kanuma.tochigi.jp":true,"karasuyama.tochigi.jp":true,"kuroiso.tochigi.jp":true,"mashiko.tochigi.jp":true,"mibu.tochigi.jp":true,"moka.tochigi.jp":true,"motegi.tochigi.jp":true,"nasu.tochigi.jp":true,"nasushiobara.tochigi.jp":true,"nikko.tochigi.jp":true,"nishikata.tochigi.jp":true,"nogi.tochigi.jp":true,"ohira.tochigi.jp":true,"ohtawara.tochigi.jp":true,"oyama.tochigi.jp":true,"sakura.tochigi.jp":true,"sano.tochigi.jp":true,"shimotsuke.tochigi.jp":true,"shioya.tochigi.jp":true,"takanezawa.tochigi.jp":true,"tochigi.tochigi.jp":true,"tsuga.tochigi.jp":true,"ujiie.tochigi.jp":true,"utsunomiya.tochigi.jp":true,"yaita.tochigi.jp":true,"aizumi.tokushima.jp":true,"anan.tokushima.jp":true,"ichiba.tokushima.jp":true,"itano.tokushima.jp":true,"kainan.tokushima.jp":true,"komatsushima.tokushima.jp":true,"matsushige.tokushima.jp":true,"mima.tokushima.jp":true,"minami.tokushima.jp":true,"miyoshi.tokushima.jp":true,"mugi.tokushima.jp":true,"nakagawa.tokushima.jp":true,"naruto.tokushima.jp":true,"sanagochi.tokushima.jp":true,"shishikui.tokushima.jp":true,"tokushima.tokushima.jp":true,"wajiki.tokushima.jp":true,"adachi.tokyo.jp":true,"akiruno.tokyo.jp":true,"akishima.tokyo.jp":true,"aogashima.tokyo.jp":true,"arakawa.tokyo.jp":true,"bunkyo.tokyo.jp":true,"chiyoda.tokyo.jp":true,"chofu.tokyo.jp":true,"chuo.tokyo.jp":true,"edogawa.tokyo.jp":true,"fuchu.tokyo.jp":true,"fussa.tokyo.jp":true,"hachijo.tokyo.jp":true,"hachioji.tokyo.jp":true,"hamura.tokyo.jp":true,"higashikurume.tokyo.jp":true,"higashimurayama.tokyo.jp":true,"higashiyamato.tokyo.jp":true,"hino.tokyo.jp":true,"hinode.tokyo.jp":true,"hinohara.tokyo.jp":true,"inagi.tokyo.jp":true,"itabashi.tokyo.jp":true,"katsushika.tokyo.jp":true,"kita.tokyo.jp":true,"kiyose.tokyo.jp":true,"kodaira.tokyo.jp":true,"koganei.tokyo.jp":true,"kokubunji.tokyo.jp":true,"komae.tokyo.jp":true,"koto.tokyo.jp":true,"kouzushima.tokyo.jp":true,"kunitachi.tokyo.jp":true,"machida.tokyo.jp":true,"meguro.tokyo.jp":true,"minato.tokyo.jp":true,"mitaka.tokyo.jp":true,"mizuho.tokyo.jp":true,"musashimurayama.tokyo.jp":true,"musashino.tokyo.jp":true,"nakano.tokyo.jp":true,"nerima.tokyo.jp":true,"ogasawara.tokyo.jp":true,"okutama.tokyo.jp":true,"ome.tokyo.jp":true,"oshima.tokyo.jp":true,"ota.tokyo.jp":true,"setagaya.tokyo.jp":true,"shibuya.tokyo.jp":true,"shinagawa.tokyo.jp":true,"shinjuku.tokyo.jp":true,"suginami.tokyo.jp":true,"sumida.tokyo.jp":true,"tachikawa.tokyo.jp":true,"taito.tokyo.jp":true,"tama.tokyo.jp":true,"toshima.tokyo.jp":true,"chizu.tottori.jp":true,"hino.tottori.jp":true,"kawahara.tottori.jp":true,"koge.tottori.jp":true,"kotoura.tottori.jp":true,"misasa.tottori.jp":true,"nanbu.tottori.jp":true,"nichinan.tottori.jp":true,"sakaiminato.tottori.jp":true,"tottori.tottori.jp":true,"wakasa.tottori.jp":true,"yazu.tottori.jp":true,"yonago.tottori.jp":true,"asahi.toyama.jp":true,"fuchu.toyama.jp":true,"fukumitsu.toyama.jp":true,"funahashi.toyama.jp":true,"himi.toyama.jp":true,"imizu.toyama.jp":true,"inami.toyama.jp":true,"johana.toyama.jp":true,"kamiichi.toyama.jp":true,"kurobe.toyama.jp":true,"nakaniikawa.toyama.jp":true,"namerikawa.toyama.jp":true,"nanto.toyama.jp":true,"nyuzen.toyama.jp":true,"oyabe.toyama.jp":true,"taira.toyama.jp":true,"takaoka.toyama.jp":true,"tateyama.toyama.jp":true,"toga.toyama.jp":true,"tonami.toyama.jp":true,"toyama.toyama.jp":true,"unazuki.toyama.jp":true,"uozu.toyama.jp":true,"yamada.toyama.jp":true,"arida.wakayama.jp":true,"aridagawa.wakayama.jp":true,"gobo.wakayama.jp":true,"hashimoto.wakayama.jp":true,"hidaka.wakayama.jp":true,"hirogawa.wakayama.jp":true,"inami.wakayama.jp":true,"iwade.wakayama.jp":true,"kainan.wakayama.jp":true,"kamitonda.wakayama.jp":true,"katsuragi.wakayama.jp":true,"kimino.wakayama.jp":true,"kinokawa.wakayama.jp":true,"kitayama.wakayama.jp":true,"koya.wakayama.jp":true,"koza.wakayama.jp":true,"kozagawa.wakayama.jp":true,"kudoyama.wakayama.jp":true,"kushimoto.wakayama.jp":true,"mihama.wakayama.jp":true,"misato.wakayama.jp":true,"nachikatsuura.wakayama.jp":true,"shingu.wakayama.jp":true,"shirahama.wakayama.jp":true,"taiji.wakayama.jp":true,"tanabe.wakayama.jp":true,"wakayama.wakayama.jp":true,"yuasa.wakayama.jp":true,"yura.wakayama.jp":true,"asahi.yamagata.jp":true,"funagata.yamagata.jp":true,"higashine.yamagata.jp":true,"iide.yamagata.jp":true,"kahoku.yamagata.jp":true,"kaminoyama.yamagata.jp":true,"kaneyama.yamagata.jp":true,"kawanishi.yamagata.jp":true,"mamurogawa.yamagata.jp":true,"mikawa.yamagata.jp":true,"murayama.yamagata.jp":true,"nagai.yamagata.jp":true,"nakayama.yamagata.jp":true,"nanyo.yamagata.jp":true,"nishikawa.yamagata.jp":true,"obanazawa.yamagata.jp":true,"oe.yamagata.jp":true,"oguni.yamagata.jp":true,"ohkura.yamagata.jp":true,"oishida.yamagata.jp":true,"sagae.yamagata.jp":true,"sakata.yamagata.jp":true,"sakegawa.yamagata.jp":true,"shinjo.yamagata.jp":true,"shirataka.yamagata.jp":true,"shonai.yamagata.jp":true,"takahata.yamagata.jp":true,"tendo.yamagata.jp":true,"tozawa.yamagata.jp":true,"tsuruoka.yamagata.jp":true,"yamagata.yamagata.jp":true,"yamanobe.yamagata.jp":true,"yonezawa.yamagata.jp":true,"yuza.yamagata.jp":true,"abu.yamaguchi.jp":true,"hagi.yamaguchi.jp":true,"hikari.yamaguchi.jp":true,"hofu.yamaguchi.jp":true,"iwakuni.yamaguchi.jp":true,"kudamatsu.yamaguchi.jp":true,"mitou.yamaguchi.jp":true,"nagato.yamaguchi.jp":true,"oshima.yamaguchi.jp":true,"shimonoseki.yamaguchi.jp":true,"shunan.yamaguchi.jp":true,"tabuse.yamaguchi.jp":true,"tokuyama.yamaguchi.jp":true,"toyota.yamaguchi.jp":true,"ube.yamaguchi.jp":true,"yuu.yamaguchi.jp":true,"chuo.yamanashi.jp":true,"doshi.yamanashi.jp":true,"fuefuki.yamanashi.jp":true,"fujikawa.yamanashi.jp":true,"fujikawaguchiko.yamanashi.jp":true,"fujiyoshida.yamanashi.jp":true,"hayakawa.yamanashi.jp":true,"hokuto.yamanashi.jp":true,"ichikawamisato.yamanashi.jp":true,"kai.yamanashi.jp":true,"kofu.yamanashi.jp":true,"koshu.yamanashi.jp":true,"kosuge.yamanashi.jp":true,"minami-alps.yamanashi.jp":true,"minobu.yamanashi.jp":true,"nakamichi.yamanashi.jp":true,"nanbu.yamanashi.jp":true,"narusawa.yamanashi.jp":true,"nirasaki.yamanashi.jp":true,"nishikatsura.yamanashi.jp":true,"oshino.yamanashi.jp":true,"otsuki.yamanashi.jp":true,"showa.yamanashi.jp":true,"tabayama.yamanashi.jp":true,"tsuru.yamanashi.jp":true,"uenohara.yamanashi.jp":true,"yamanakako.yamanashi.jp":true,"yamanashi.yamanashi.jp":true,"*.ke":true,"kg":true,"org.kg":true,"net.kg":true,"com.kg":true,"edu.kg":true,"gov.kg":true,"mil.kg":true,"*.kh":true,"ki":true,"edu.ki":true,"biz.ki":true,"net.ki":true,"org.ki":true,"gov.ki":true,"info.ki":true,"com.ki":true,"km":true,"org.km":true,"nom.km":true,"gov.km":true,"prd.km":true,"tm.km":true,"edu.km":true,"mil.km":true,"ass.km":true,"com.km":true,"coop.km":true,"asso.km":true,"presse.km":true,"medecin.km":true,"notaires.km":true,"pharmaciens.km":true,"veterinaire.km":true,"gouv.km":true,"kn":true,"net.kn":true,"org.kn":true,"edu.kn":true,"gov.kn":true,"kp":true,"com.kp":true,"edu.kp":true,"gov.kp":true,"org.kp":true,"rep.kp":true,"tra.kp":true,"kr":true,"ac.kr":true,"co.kr":true,"es.kr":true,"go.kr":true,"hs.kr":true,"kg.kr":true,"mil.kr":true,"ms.kr":true,"ne.kr":true,"or.kr":true,"pe.kr":true,"re.kr":true,"sc.kr":true,"busan.kr":true,"chungbuk.kr":true,"chungnam.kr":true,"daegu.kr":true,"daejeon.kr":true,"gangwon.kr":true,"gwangju.kr":true,"gyeongbuk.kr":true,"gyeonggi.kr":true,"gyeongnam.kr":true,"incheon.kr":true,"jeju.kr":true,"jeonbuk.kr":true,"jeonnam.kr":true,"seoul.kr":true,"ulsan.kr":true,"*.kw":true,"ky":true,"edu.ky":true,"gov.ky":true,"com.ky":true,"org.ky":true,"net.ky":true,"kz":true,"org.kz":true,"edu.kz":true,"net.kz":true,"gov.kz":true,"mil.kz":true,"com.kz":true,"la":true,"int.la":true,"net.la":true,"info.la":true,"edu.la":true,"gov.la":true,"per.la":true,"com.la":true,"org.la":true,"lb":true,"com.lb":true,"edu.lb":true,"gov.lb":true,"net.lb":true,"org.lb":true,"lc":true,"com.lc":true,"net.lc":true,"co.lc":true,"org.lc":true,"edu.lc":true,"gov.lc":true,"li":true,"lk":true,"gov.lk":true,"sch.lk":true,"net.lk":true,"int.lk":true,"com.lk":true,"org.lk":true,"edu.lk":true,"ngo.lk":true,"soc.lk":true,"web.lk":true,"ltd.lk":true,"assn.lk":true,"grp.lk":true,"hotel.lk":true,"lr":true,"com.lr":true,"edu.lr":true,"gov.lr":true,"org.lr":true,"net.lr":true,"ls":true,"co.ls":true,"org.ls":true,"lt":true,"gov.lt":true,"lu":true,"lv":true,"com.lv":true,"edu.lv":true,"gov.lv":true,"org.lv":true,"mil.lv":true,"id.lv":true,"net.lv":true,"asn.lv":true,"conf.lv":true,"ly":true,"com.ly":true,"net.ly":true,"gov.ly":true,"plc.ly":true,"edu.ly":true,"sch.ly":true,"med.ly":true,"org.ly":true,"id.ly":true,"ma":true,"co.ma":true,"net.ma":true,"gov.ma":true,"org.ma":true,"ac.ma":true,"press.ma":true,"mc":true,"tm.mc":true,"asso.mc":true,"md":true,"me":true,"co.me":true,"net.me":true,"org.me":true,"edu.me":true,"ac.me":true,"gov.me":true,"its.me":true,"priv.me":true,"mg":true,"org.mg":true,"nom.mg":true,"gov.mg":true,"prd.mg":true,"tm.mg":true,"edu.mg":true,"mil.mg":true,"com.mg":true,"mh":true,"mil":true,"mk":true,"com.mk":true,"org.mk":true,"net.mk":true,"edu.mk":true,"gov.mk":true,"inf.mk":true,"name.mk":true,"ml":true,"com.ml":true,"edu.ml":true,"gouv.ml":true,"gov.ml":true,"net.ml":true,"org.ml":true,"presse.ml":true,"*.mm":true,"mn":true,"gov.mn":true,"edu.mn":true,"org.mn":true,"mo":true,"com.mo":true,"net.mo":true,"org.mo":true,"edu.mo":true,"gov.mo":true,"mobi":true,"mp":true,"mq":true,"mr":true,"gov.mr":true,"ms":true,"com.ms":true,"edu.ms":true,"gov.ms":true,"net.ms":true,"org.ms":true,"mt":true,"com.mt":true,"edu.mt":true,"net.mt":true,"org.mt":true,"mu":true,"com.mu":true,"net.mu":true,"org.mu":true,"gov.mu":true,"ac.mu":true,"co.mu":true,"or.mu":true,"museum":true,"academy.museum":true,"agriculture.museum":true,"air.museum":true,"airguard.museum":true,"alabama.museum":true,"alaska.museum":true,"amber.museum":true,"ambulance.museum":true,"american.museum":true,"americana.museum":true,"americanantiques.museum":true,"americanart.museum":true,"amsterdam.museum":true,"and.museum":true,"annefrank.museum":true,"anthro.museum":true,"anthropology.museum":true,"antiques.museum":true,"aquarium.museum":true,"arboretum.museum":true,"archaeological.museum":true,"archaeology.museum":true,"architecture.museum":true,"art.museum":true,"artanddesign.museum":true,"artcenter.museum":true,"artdeco.museum":true,"arteducation.museum":true,"artgallery.museum":true,"arts.museum":true,"artsandcrafts.museum":true,"asmatart.museum":true,"assassination.museum":true,"assisi.museum":true,"association.museum":true,"astronomy.museum":true,"atlanta.museum":true,"austin.museum":true,"australia.museum":true,"automotive.museum":true,"aviation.museum":true,"axis.museum":true,"badajoz.museum":true,"baghdad.museum":true,"bahn.museum":true,"bale.museum":true,"baltimore.museum":true,"barcelona.museum":true,"baseball.museum":true,"basel.museum":true,"baths.museum":true,"bauern.museum":true,"beauxarts.museum":true,"beeldengeluid.museum":true,"bellevue.museum":true,"bergbau.museum":true,"berkeley.museum":true,"berlin.museum":true,"bern.museum":true,"bible.museum":true,"bilbao.museum":true,"bill.museum":true,"birdart.museum":true,"birthplace.museum":true,"bonn.museum":true,"boston.museum":true,"botanical.museum":true,"botanicalgarden.museum":true,"botanicgarden.museum":true,"botany.museum":true,"brandywinevalley.museum":true,"brasil.museum":true,"bristol.museum":true,"british.museum":true,"britishcolumbia.museum":true,"broadcast.museum":true,"brunel.museum":true,"brussel.museum":true,"brussels.museum":true,"bruxelles.museum":true,"building.museum":true,"burghof.museum":true,"bus.museum":true,"bushey.museum":true,"cadaques.museum":true,"california.museum":true,"cambridge.museum":true,"can.museum":true,"canada.museum":true,"capebreton.museum":true,"carrier.museum":true,"cartoonart.museum":true,"casadelamoneda.museum":true,"castle.museum":true,"castres.museum":true,"celtic.museum":true,"center.museum":true,"chattanooga.museum":true,"cheltenham.museum":true,"chesapeakebay.museum":true,"chicago.museum":true,"children.museum":true,"childrens.museum":true,"childrensgarden.museum":true,"chiropractic.museum":true,"chocolate.museum":true,"christiansburg.museum":true,"cincinnati.museum":true,"cinema.museum":true,"circus.museum":true,"civilisation.museum":true,"civilization.museum":true,"civilwar.museum":true,"clinton.museum":true,"clock.museum":true,"coal.museum":true,"coastaldefence.museum":true,"cody.museum":true,"coldwar.museum":true,"collection.museum":true,"colonialwilliamsburg.museum":true,"coloradoplateau.museum":true,"columbia.museum":true,"columbus.museum":true,"communication.museum":true,"communications.museum":true,"community.museum":true,"computer.museum":true,"computerhistory.museum":true,"xn--comunicaes-v6a2o.museum":true,"contemporary.museum":true,"contemporaryart.museum":true,"convent.museum":true,"copenhagen.museum":true,"corporation.museum":true,"xn--correios-e-telecomunicaes-ghc29a.museum":true,"corvette.museum":true,"costume.museum":true,"countryestate.museum":true,"county.museum":true,"crafts.museum":true,"cranbrook.museum":true,"creation.museum":true,"cultural.museum":true,"culturalcenter.museum":true,"culture.museum":true,"cyber.museum":true,"cymru.museum":true,"dali.museum":true,"dallas.museum":true,"database.museum":true,"ddr.museum":true,"decorativearts.museum":true,"delaware.museum":true,"delmenhorst.museum":true,"denmark.museum":true,"depot.museum":true,"design.museum":true,"detroit.museum":true,"dinosaur.museum":true,"discovery.museum":true,"dolls.museum":true,"donostia.museum":true,"durham.museum":true,"eastafrica.museum":true,"eastcoast.museum":true,"education.museum":true,"educational.museum":true,"egyptian.museum":true,"eisenbahn.museum":true,"elburg.museum":true,"elvendrell.museum":true,"embroidery.museum":true,"encyclopedic.museum":true,"england.museum":true,"entomology.museum":true,"environment.museum":true,"environmentalconservation.museum":true,"epilepsy.museum":true,"essex.museum":true,"estate.museum":true,"ethnology.museum":true,"exeter.museum":true,"exhibition.museum":true,"family.museum":true,"farm.museum":true,"farmequipment.museum":true,"farmers.museum":true,"farmstead.museum":true,"field.museum":true,"figueres.museum":true,"filatelia.museum":true,"film.museum":true,"fineart.museum":true,"finearts.museum":true,"finland.museum":true,"flanders.museum":true,"florida.museum":true,"force.museum":true,"fortmissoula.museum":true,"fortworth.museum":true,"foundation.museum":true,"francaise.museum":true,"frankfurt.museum":true,"franziskaner.museum":true,"freemasonry.museum":true,"freiburg.museum":true,"fribourg.museum":true,"frog.museum":true,"fundacio.museum":true,"furniture.museum":true,"gallery.museum":true,"garden.museum":true,"gateway.museum":true,"geelvinck.museum":true,"gemological.museum":true,"geology.museum":true,"georgia.museum":true,"giessen.museum":true,"glas.museum":true,"glass.museum":true,"gorge.museum":true,"grandrapids.museum":true,"graz.museum":true,"guernsey.museum":true,"halloffame.museum":true,"hamburg.museum":true,"handson.museum":true,"harvestcelebration.museum":true,"hawaii.museum":true,"health.museum":true,"heimatunduhren.museum":true,"hellas.museum":true,"helsinki.museum":true,"hembygdsforbund.museum":true,"heritage.museum":true,"histoire.museum":true,"historical.museum":true,"historicalsociety.museum":true,"historichouses.museum":true,"historisch.museum":true,"historisches.museum":true,"history.museum":true,"historyofscience.museum":true,"horology.museum":true,"house.museum":true,"humanities.museum":true,"illustration.museum":true,"imageandsound.museum":true,"indian.museum":true,"indiana.museum":true,"indianapolis.museum":true,"indianmarket.museum":true,"intelligence.museum":true,"interactive.museum":true,"iraq.museum":true,"iron.museum":true,"isleofman.museum":true,"jamison.museum":true,"jefferson.museum":true,"jerusalem.museum":true,"jewelry.museum":true,"jewish.museum":true,"jewishart.museum":true,"jfk.museum":true,"journalism.museum":true,"judaica.museum":true,"judygarland.museum":true,"juedisches.museum":true,"juif.museum":true,"karate.museum":true,"karikatur.museum":true,"kids.museum":true,"koebenhavn.museum":true,"koeln.museum":true,"kunst.museum":true,"kunstsammlung.museum":true,"kunstunddesign.museum":true,"labor.museum":true,"labour.museum":true,"lajolla.museum":true,"lancashire.museum":true,"landes.museum":true,"lans.museum":true,"xn--lns-qla.museum":true,"larsson.museum":true,"lewismiller.museum":true,"lincoln.museum":true,"linz.museum":true,"living.museum":true,"livinghistory.museum":true,"localhistory.museum":true,"london.museum":true,"losangeles.museum":true,"louvre.museum":true,"loyalist.museum":true,"lucerne.museum":true,"luxembourg.museum":true,"luzern.museum":true,"mad.museum":true,"madrid.museum":true,"mallorca.museum":true,"manchester.museum":true,"mansion.museum":true,"mansions.museum":true,"manx.museum":true,"marburg.museum":true,"maritime.museum":true,"maritimo.museum":true,"maryland.museum":true,"marylhurst.museum":true,"media.museum":true,"medical.museum":true,"medizinhistorisches.museum":true,"meeres.museum":true,"memorial.museum":true,"mesaverde.museum":true,"michigan.museum":true,"midatlantic.museum":true,"military.museum":true,"mill.museum":true,"miners.museum":true,"mining.museum":true,"minnesota.museum":true,"missile.museum":true,"missoula.museum":true,"modern.museum":true,"moma.museum":true,"money.museum":true,"monmouth.museum":true,"monticello.museum":true,"montreal.museum":true,"moscow.museum":true,"motorcycle.museum":true,"muenchen.museum":true,"muenster.museum":true,"mulhouse.museum":true,"muncie.museum":true,"museet.museum":true,"museumcenter.museum":true,"museumvereniging.museum":true,"music.museum":true,"national.museum":true,"nationalfirearms.museum":true,"nationalheritage.museum":true,"nativeamerican.museum":true,"naturalhistory.museum":true,"naturalhistorymuseum.museum":true,"naturalsciences.museum":true,"nature.museum":true,"naturhistorisches.museum":true,"natuurwetenschappen.museum":true,"naumburg.museum":true,"naval.museum":true,"nebraska.museum":true,"neues.museum":true,"newhampshire.museum":true,"newjersey.museum":true,"newmexico.museum":true,"newport.museum":true,"newspaper.museum":true,"newyork.museum":true,"niepce.museum":true,"norfolk.museum":true,"north.museum":true,"nrw.museum":true,"nuernberg.museum":true,"nuremberg.museum":true,"nyc.museum":true,"nyny.museum":true,"oceanographic.museum":true,"oceanographique.museum":true,"omaha.museum":true,"online.museum":true,"ontario.museum":true,"openair.museum":true,"oregon.museum":true,"oregontrail.museum":true,"otago.museum":true,"oxford.museum":true,"pacific.museum":true,"paderborn.museum":true,"palace.museum":true,"paleo.museum":true,"palmsprings.museum":true,"panama.museum":true,"paris.museum":true,"pasadena.museum":true,"pharmacy.museum":true,"philadelphia.museum":true,"philadelphiaarea.museum":true,"philately.museum":true,"phoenix.museum":true,"photography.museum":true,"pilots.museum":true,"pittsburgh.museum":true,"planetarium.museum":true,"plantation.museum":true,"plants.museum":true,"plaza.museum":true,"portal.museum":true,"portland.museum":true,"portlligat.museum":true,"posts-and-telecommunications.museum":true,"preservation.museum":true,"presidio.museum":true,"press.museum":true,"project.museum":true,"public.museum":true,"pubol.museum":true,"quebec.museum":true,"railroad.museum":true,"railway.museum":true,"research.museum":true,"resistance.museum":true,"riodejaneiro.museum":true,"rochester.museum":true,"rockart.museum":true,"roma.museum":true,"russia.museum":true,"saintlouis.museum":true,"salem.museum":true,"salvadordali.museum":true,"salzburg.museum":true,"sandiego.museum":true,"sanfrancisco.museum":true,"santabarbara.museum":true,"santacruz.museum":true,"santafe.museum":true,"saskatchewan.museum":true,"satx.museum":true,"savannahga.museum":true,"schlesisches.museum":true,"schoenbrunn.museum":true,"schokoladen.museum":true,"school.museum":true,"schweiz.museum":true,"science.museum":true,"scienceandhistory.museum":true,"scienceandindustry.museum":true,"sciencecenter.museum":true,"sciencecenters.museum":true,"science-fiction.museum":true,"sciencehistory.museum":true,"sciences.museum":true,"sciencesnaturelles.museum":true,"scotland.museum":true,"seaport.museum":true,"settlement.museum":true,"settlers.museum":true,"shell.museum":true,"sherbrooke.museum":true,"sibenik.museum":true,"silk.museum":true,"ski.museum":true,"skole.museum":true,"society.museum":true,"sologne.museum":true,"soundandvision.museum":true,"southcarolina.museum":true,"southwest.museum":true,"space.museum":true,"spy.museum":true,"square.museum":true,"stadt.museum":true,"stalbans.museum":true,"starnberg.museum":true,"state.museum":true,"stateofdelaware.museum":true,"station.museum":true,"steam.museum":true,"steiermark.museum":true,"stjohn.museum":true,"stockholm.museum":true,"stpetersburg.museum":true,"stuttgart.museum":true,"suisse.museum":true,"surgeonshall.museum":true,"surrey.museum":true,"svizzera.museum":true,"sweden.museum":true,"sydney.museum":true,"tank.museum":true,"tcm.museum":true,"technology.museum":true,"telekommunikation.museum":true,"television.museum":true,"texas.museum":true,"textile.museum":true,"theater.museum":true,"time.museum":true,"timekeeping.museum":true,"topology.museum":true,"torino.museum":true,"touch.museum":true,"town.museum":true,"transport.museum":true,"tree.museum":true,"trolley.museum":true,"trust.museum":true,"trustee.museum":true,"uhren.museum":true,"ulm.museum":true,"undersea.museum":true,"university.museum":true,"usa.museum":true,"usantiques.museum":true,"usarts.museum":true,"uscountryestate.museum":true,"usculture.museum":true,"usdecorativearts.museum":true,"usgarden.museum":true,"ushistory.museum":true,"ushuaia.museum":true,"uslivinghistory.museum":true,"utah.museum":true,"uvic.museum":true,"valley.museum":true,"vantaa.museum":true,"versailles.museum":true,"viking.museum":true,"village.museum":true,"virginia.museum":true,"virtual.museum":true,"virtuel.museum":true,"vlaanderen.museum":true,"volkenkunde.museum":true,"wales.museum":true,"wallonie.museum":true,"war.museum":true,"washingtondc.museum":true,"watchandclock.museum":true,"watch-and-clock.museum":true,"western.museum":true,"westfalen.museum":true,"whaling.museum":true,"wildlife.museum":true,"williamsburg.museum":true,"windmill.museum":true,"workshop.museum":true,"york.museum":true,"yorkshire.museum":true,"yosemite.museum":true,"youth.museum":true,"zoological.museum":true,"zoology.museum":true,"xn--9dbhblg6di.museum":true,"xn--h1aegh.museum":true,"mv":true,"aero.mv":true,"biz.mv":true,"com.mv":true,"coop.mv":true,"edu.mv":true,"gov.mv":true,"info.mv":true,"int.mv":true,"mil.mv":true,"museum.mv":true,"name.mv":true,"net.mv":true,"org.mv":true,"pro.mv":true,"mw":true,"ac.mw":true,"biz.mw":true,"co.mw":true,"com.mw":true,"coop.mw":true,"edu.mw":true,"gov.mw":true,"int.mw":true,"museum.mw":true,"net.mw":true,"org.mw":true,"mx":true,"com.mx":true,"org.mx":true,"gob.mx":true,"edu.mx":true,"net.mx":true,"my":true,"com.my":true,"net.my":true,"org.my":true,"gov.my":true,"edu.my":true,"mil.my":true,"name.my":true,"*.mz":true,"teledata.mz":false,"na":true,"info.na":true,"pro.na":true,"name.na":true,"school.na":true,"or.na":true,"dr.na":true,"us.na":true,"mx.na":true,"ca.na":true,"in.na":true,"cc.na":true,"tv.na":true,"ws.na":true,"mobi.na":true,"co.na":true,"com.na":true,"org.na":true,"name":true,"nc":true,"asso.nc":true,"ne":true,"net":true,"nf":true,"com.nf":true,"net.nf":true,"per.nf":true,"rec.nf":true,"web.nf":true,"arts.nf":true,"firm.nf":true,"info.nf":true,"other.nf":true,"store.nf":true,"ng":true,"com.ng":true,"edu.ng":true,"name.ng":true,"net.ng":true,"org.ng":true,"sch.ng":true,"gov.ng":true,"mil.ng":true,"mobi.ng":true,"*.ni":true,"nl":true,"bv.nl":true,"no":true,"fhs.no":true,"vgs.no":true,"fylkesbibl.no":true,"folkebibl.no":true,"museum.no":true,"idrett.no":true,"priv.no":true,"mil.no":true,"stat.no":true,"dep.no":true,"kommune.no":true,"herad.no":true,"aa.no":true,"ah.no":true,"bu.no":true,"fm.no":true,"hl.no":true,"hm.no":true,"jan-mayen.no":true,"mr.no":true,"nl.no":true,"nt.no":true,"of.no":true,"ol.no":true,"oslo.no":true,"rl.no":true,"sf.no":true,"st.no":true,"svalbard.no":true,"tm.no":true,"tr.no":true,"va.no":true,"vf.no":true,"gs.aa.no":true,"gs.ah.no":true,"gs.bu.no":true,"gs.fm.no":true,"gs.hl.no":true,"gs.hm.no":true,"gs.jan-mayen.no":true,"gs.mr.no":true,"gs.nl.no":true,"gs.nt.no":true,"gs.of.no":true,"gs.ol.no":true,"gs.oslo.no":true,"gs.rl.no":true,"gs.sf.no":true,"gs.st.no":true,"gs.svalbard.no":true,"gs.tm.no":true,"gs.tr.no":true,"gs.va.no":true,"gs.vf.no":true,"akrehamn.no":true,"xn--krehamn-dxa.no":true,"algard.no":true,"xn--lgrd-poac.no":true,"arna.no":true,"brumunddal.no":true,"bryne.no":true,"bronnoysund.no":true,"xn--brnnysund-m8ac.no":true,"drobak.no":true,"xn--drbak-wua.no":true,"egersund.no":true,"fetsund.no":true,"floro.no":true,"xn--flor-jra.no":true,"fredrikstad.no":true,"hokksund.no":true,"honefoss.no":true,"xn--hnefoss-q1a.no":true,"jessheim.no":true,"jorpeland.no":true,"xn--jrpeland-54a.no":true,"kirkenes.no":true,"kopervik.no":true,"krokstadelva.no":true,"langevag.no":true,"xn--langevg-jxa.no":true,"leirvik.no":true,"mjondalen.no":true,"xn--mjndalen-64a.no":true,"mo-i-rana.no":true,"mosjoen.no":true,"xn--mosjen-eya.no":true,"nesoddtangen.no":true,"orkanger.no":true,"osoyro.no":true,"xn--osyro-wua.no":true,"raholt.no":true,"xn--rholt-mra.no":true,"sandnessjoen.no":true,"xn--sandnessjen-ogb.no":true,"skedsmokorset.no":true,"slattum.no":true,"spjelkavik.no":true,"stathelle.no":true,"stavern.no":true,"stjordalshalsen.no":true,"xn--stjrdalshalsen-sqb.no":true,"tananger.no":true,"tranby.no":true,"vossevangen.no":true,"afjord.no":true,"xn--fjord-lra.no":true,"agdenes.no":true,"al.no":true,"xn--l-1fa.no":true,"alesund.no":true,"xn--lesund-hua.no":true,"alstahaug.no":true,"alta.no":true,"xn--lt-liac.no":true,"alaheadju.no":true,"xn--laheadju-7ya.no":true,"alvdal.no":true,"amli.no":true,"xn--mli-tla.no":true,"amot.no":true,"xn--mot-tla.no":true,"andebu.no":true,"andoy.no":true,"xn--andy-ira.no":true,"andasuolo.no":true,"ardal.no":true,"xn--rdal-poa.no":true,"aremark.no":true,"arendal.no":true,"xn--s-1fa.no":true,"aseral.no":true,"xn--seral-lra.no":true,"asker.no":true,"askim.no":true,"askvoll.no":true,"askoy.no":true,"xn--asky-ira.no":true,"asnes.no":true,"xn--snes-poa.no":true,"audnedaln.no":true,"aukra.no":true,"aure.no":true,"aurland.no":true,"aurskog-holand.no":true,"xn--aurskog-hland-jnb.no":true,"austevoll.no":true,"austrheim.no":true,"averoy.no":true,"xn--avery-yua.no":true,"balestrand.no":true,"ballangen.no":true,"balat.no":true,"xn--blt-elab.no":true,"balsfjord.no":true,"bahccavuotna.no":true,"xn--bhccavuotna-k7a.no":true,"bamble.no":true,"bardu.no":true,"beardu.no":true,"beiarn.no":true,"bajddar.no":true,"xn--bjddar-pta.no":true,"baidar.no":true,"xn--bidr-5nac.no":true,"berg.no":true,"bergen.no":true,"berlevag.no":true,"xn--berlevg-jxa.no":true,"bearalvahki.no":true,"xn--bearalvhki-y4a.no":true,"bindal.no":true,"birkenes.no":true,"bjarkoy.no":true,"xn--bjarky-fya.no":true,"bjerkreim.no":true,"bjugn.no":true,"bodo.no":true,"xn--bod-2na.no":true,"badaddja.no":true,"xn--bdddj-mrabd.no":true,"budejju.no":true,"bokn.no":true,"bremanger.no":true,"bronnoy.no":true,"xn--brnny-wuac.no":true,"bygland.no":true,"bykle.no":true,"barum.no":true,"xn--brum-voa.no":true,"bo.telemark.no":true,"xn--b-5ga.telemark.no":true,"bo.nordland.no":true,"xn--b-5ga.nordland.no":true,"bievat.no":true,"xn--bievt-0qa.no":true,"bomlo.no":true,"xn--bmlo-gra.no":true,"batsfjord.no":true,"xn--btsfjord-9za.no":true,"bahcavuotna.no":true,"xn--bhcavuotna-s4a.no":true,"dovre.no":true,"drammen.no":true,"drangedal.no":true,"dyroy.no":true,"xn--dyry-ira.no":true,"donna.no":true,"xn--dnna-gra.no":true,"eid.no":true,"eidfjord.no":true,"eidsberg.no":true,"eidskog.no":true,"eidsvoll.no":true,"eigersund.no":true,"elverum.no":true,"enebakk.no":true,"engerdal.no":true,"etne.no":true,"etnedal.no":true,"evenes.no":true,"evenassi.no":true,"xn--eveni-0qa01ga.no":true,"evje-og-hornnes.no":true,"farsund.no":true,"fauske.no":true,"fuossko.no":true,"fuoisku.no":true,"fedje.no":true,"fet.no":true,"finnoy.no":true,"xn--finny-yua.no":true,"fitjar.no":true,"fjaler.no":true,"fjell.no":true,"flakstad.no":true,"flatanger.no":true,"flekkefjord.no":true,"flesberg.no":true,"flora.no":true,"fla.no":true,"xn--fl-zia.no":true,"folldal.no":true,"forsand.no":true,"fosnes.no":true,"frei.no":true,"frogn.no":true,"froland.no":true,"frosta.no":true,"frana.no":true,"xn--frna-woa.no":true,"froya.no":true,"xn--frya-hra.no":true,"fusa.no":true,"fyresdal.no":true,"forde.no":true,"xn--frde-gra.no":true,"gamvik.no":true,"gangaviika.no":true,"xn--ggaviika-8ya47h.no":true,"gaular.no":true,"gausdal.no":true,"gildeskal.no":true,"xn--gildeskl-g0a.no":true,"giske.no":true,"gjemnes.no":true,"gjerdrum.no":true,"gjerstad.no":true,"gjesdal.no":true,"gjovik.no":true,"xn--gjvik-wua.no":true,"gloppen.no":true,"gol.no":true,"gran.no":true,"grane.no":true,"granvin.no":true,"gratangen.no":true,"grimstad.no":true,"grong.no":true,"kraanghke.no":true,"xn--kranghke-b0a.no":true,"grue.no":true,"gulen.no":true,"hadsel.no":true,"halden.no":true,"halsa.no":true,"hamar.no":true,"hamaroy.no":true,"habmer.no":true,"xn--hbmer-xqa.no":true,"hapmir.no":true,"xn--hpmir-xqa.no":true,"hammerfest.no":true,"hammarfeasta.no":true,"xn--hmmrfeasta-s4ac.no":true,"haram.no":true,"hareid.no":true,"harstad.no":true,"hasvik.no":true,"aknoluokta.no":true,"xn--koluokta-7ya57h.no":true,"hattfjelldal.no":true,"aarborte.no":true,"haugesund.no":true,"hemne.no":true,"hemnes.no":true,"hemsedal.no":true,"heroy.more-og-romsdal.no":true,"xn--hery-ira.xn--mre-og-romsdal-qqb.no":true,"heroy.nordland.no":true,"xn--hery-ira.nordland.no":true,"hitra.no":true,"hjartdal.no":true,"hjelmeland.no":true,"hobol.no":true,"xn--hobl-ira.no":true,"hof.no":true,"hol.no":true,"hole.no":true,"holmestrand.no":true,"holtalen.no":true,"xn--holtlen-hxa.no":true,"hornindal.no":true,"horten.no":true,"hurdal.no":true,"hurum.no":true,"hvaler.no":true,"hyllestad.no":true,"hagebostad.no":true,"xn--hgebostad-g3a.no":true,"hoyanger.no":true,"xn--hyanger-q1a.no":true,"hoylandet.no":true,"xn--hylandet-54a.no":true,"ha.no":true,"xn--h-2fa.no":true,"ibestad.no":true,"inderoy.no":true,"xn--indery-fya.no":true,"iveland.no":true,"jevnaker.no":true,"jondal.no":true,"jolster.no":true,"xn--jlster-bya.no":true,"karasjok.no":true,"karasjohka.no":true,"xn--krjohka-hwab49j.no":true,"karlsoy.no":true,"galsa.no":true,"xn--gls-elac.no":true,"karmoy.no":true,"xn--karmy-yua.no":true,"kautokeino.no":true,"guovdageaidnu.no":true,"klepp.no":true,"klabu.no":true,"xn--klbu-woa.no":true,"kongsberg.no":true,"kongsvinger.no":true,"kragero.no":true,"xn--krager-gya.no":true,"kristiansand.no":true,"kristiansund.no":true,"krodsherad.no":true,"xn--krdsherad-m8a.no":true,"kvalsund.no":true,"rahkkeravju.no":true,"xn--rhkkervju-01af.no":true,"kvam.no":true,"kvinesdal.no":true,"kvinnherad.no":true,"kviteseid.no":true,"kvitsoy.no":true,"xn--kvitsy-fya.no":true,"kvafjord.no":true,"xn--kvfjord-nxa.no":true,"giehtavuoatna.no":true,"kvanangen.no":true,"xn--kvnangen-k0a.no":true,"navuotna.no":true,"xn--nvuotna-hwa.no":true,"kafjord.no":true,"xn--kfjord-iua.no":true,"gaivuotna.no":true,"xn--givuotna-8ya.no":true,"larvik.no":true,"lavangen.no":true,"lavagis.no":true,"loabat.no":true,"xn--loabt-0qa.no":true,"lebesby.no":true,"davvesiida.no":true,"leikanger.no":true,"leirfjord.no":true,"leka.no":true,"leksvik.no":true,"lenvik.no":true,"leangaviika.no":true,"xn--leagaviika-52b.no":true,"lesja.no":true,"levanger.no":true,"lier.no":true,"lierne.no":true,"lillehammer.no":true,"lillesand.no":true,"lindesnes.no":true,"lindas.no":true,"xn--linds-pra.no":true,"lom.no":true,"loppa.no":true,"lahppi.no":true,"xn--lhppi-xqa.no":true,"lund.no":true,"lunner.no":true,"luroy.no":true,"xn--lury-ira.no":true,"luster.no":true,"lyngdal.no":true,"lyngen.no":true,"ivgu.no":true,"lardal.no":true,"lerdal.no":true,"xn--lrdal-sra.no":true,"lodingen.no":true,"xn--ldingen-q1a.no":true,"lorenskog.no":true,"xn--lrenskog-54a.no":true,"loten.no":true,"xn--lten-gra.no":true,"malvik.no":true,"masoy.no":true,"xn--msy-ula0h.no":true,"muosat.no":true,"xn--muost-0qa.no":true,"mandal.no":true,"marker.no":true,"marnardal.no":true,"masfjorden.no":true,"meland.no":true,"meldal.no":true,"melhus.no":true,"meloy.no":true,"xn--mely-ira.no":true,"meraker.no":true,"xn--merker-kua.no":true,"moareke.no":true,"xn--moreke-jua.no":true,"midsund.no":true,"midtre-gauldal.no":true,"modalen.no":true,"modum.no":true,"molde.no":true,"moskenes.no":true,"moss.no":true,"mosvik.no":true,"malselv.no":true,"xn--mlselv-iua.no":true,"malatvuopmi.no":true,"xn--mlatvuopmi-s4a.no":true,"namdalseid.no":true,"aejrie.no":true,"namsos.no":true,"namsskogan.no":true,"naamesjevuemie.no":true,"xn--nmesjevuemie-tcba.no":true,"laakesvuemie.no":true,"nannestad.no":true,"narvik.no":true,"narviika.no":true,"naustdal.no":true,"nedre-eiker.no":true,"nes.akershus.no":true,"nes.buskerud.no":true,"nesna.no":true,"nesodden.no":true,"nesseby.no":true,"unjarga.no":true,"xn--unjrga-rta.no":true,"nesset.no":true,"nissedal.no":true,"nittedal.no":true,"nord-aurdal.no":true,"nord-fron.no":true,"nord-odal.no":true,"norddal.no":true,"nordkapp.no":true,"davvenjarga.no":true,"xn--davvenjrga-y4a.no":true,"nordre-land.no":true,"nordreisa.no":true,"raisa.no":true,"xn--risa-5na.no":true,"nore-og-uvdal.no":true,"notodden.no":true,"naroy.no":true,"xn--nry-yla5g.no":true,"notteroy.no":true,"xn--nttery-byae.no":true,"odda.no":true,"oksnes.no":true,"xn--ksnes-uua.no":true,"oppdal.no":true,"oppegard.no":true,"xn--oppegrd-ixa.no":true,"orkdal.no":true,"orland.no":true,"xn--rland-uua.no":true,"orskog.no":true,"xn--rskog-uua.no":true,"orsta.no":true,"xn--rsta-fra.no":true,"os.hedmark.no":true,"os.hordaland.no":true,"osen.no":true,"osteroy.no":true,"xn--ostery-fya.no":true,"ostre-toten.no":true,"xn--stre-toten-zcb.no":true,"overhalla.no":true,"ovre-eiker.no":true,"xn--vre-eiker-k8a.no":true,"oyer.no":true,"xn--yer-zna.no":true,"oygarden.no":true,"xn--ygarden-p1a.no":true,"oystre-slidre.no":true,"xn--ystre-slidre-ujb.no":true,"porsanger.no":true,"porsangu.no":true,"xn--porsgu-sta26f.no":true,"porsgrunn.no":true,"radoy.no":true,"xn--rady-ira.no":true,"rakkestad.no":true,"rana.no":true,"ruovat.no":true,"randaberg.no":true,"rauma.no":true,"rendalen.no":true,"rennebu.no":true,"rennesoy.no":true,"xn--rennesy-v1a.no":true,"rindal.no":true,"ringebu.no":true,"ringerike.no":true,"ringsaker.no":true,"rissa.no":true,"risor.no":true,"xn--risr-ira.no":true,"roan.no":true,"rollag.no":true,"rygge.no":true,"ralingen.no":true,"xn--rlingen-mxa.no":true,"rodoy.no":true,"xn--rdy-0nab.no":true,"romskog.no":true,"xn--rmskog-bya.no":true,"roros.no":true,"xn--rros-gra.no":true,"rost.no":true,"xn--rst-0na.no":true,"royken.no":true,"xn--ryken-vua.no":true,"royrvik.no":true,"xn--ryrvik-bya.no":true,"rade.no":true,"xn--rde-ula.no":true,"salangen.no":true,"siellak.no":true,"saltdal.no":true,"salat.no":true,"xn--slt-elab.no":true,"xn--slat-5na.no":true,"samnanger.no":true,"sande.more-og-romsdal.no":true,"sande.xn--mre-og-romsdal-qqb.no":true,"sande.vestfold.no":true,"sandefjord.no":true,"sandnes.no":true,"sandoy.no":true,"xn--sandy-yua.no":true,"sarpsborg.no":true,"sauda.no":true,"sauherad.no":true,"sel.no":true,"selbu.no":true,"selje.no":true,"seljord.no":true,"sigdal.no":true,"siljan.no":true,"sirdal.no":true,"skaun.no":true,"skedsmo.no":true,"ski.no":true,"skien.no":true,"skiptvet.no":true,"skjervoy.no":true,"xn--skjervy-v1a.no":true,"skierva.no":true,"xn--skierv-uta.no":true,"skjak.no":true,"xn--skjk-soa.no":true,"skodje.no":true,"skanland.no":true,"xn--sknland-fxa.no":true,"skanit.no":true,"xn--sknit-yqa.no":true,"smola.no":true,"xn--smla-hra.no":true,"snillfjord.no":true,"snasa.no":true,"xn--snsa-roa.no":true,"snoasa.no":true,"snaase.no":true,"xn--snase-nra.no":true,"sogndal.no":true,"sokndal.no":true,"sola.no":true,"solund.no":true,"songdalen.no":true,"sortland.no":true,"spydeberg.no":true,"stange.no":true,"stavanger.no":true,"steigen.no":true,"steinkjer.no":true,"stjordal.no":true,"xn--stjrdal-s1a.no":true,"stokke.no":true,"stor-elvdal.no":true,"stord.no":true,"stordal.no":true,"storfjord.no":true,"omasvuotna.no":true,"strand.no":true,"stranda.no":true,"stryn.no":true,"sula.no":true,"suldal.no":true,"sund.no":true,"sunndal.no":true,"surnadal.no":true,"sveio.no":true,"svelvik.no":true,"sykkylven.no":true,"sogne.no":true,"xn--sgne-gra.no":true,"somna.no":true,"xn--smna-gra.no":true,"sondre-land.no":true,"xn--sndre-land-0cb.no":true,"sor-aurdal.no":true,"xn--sr-aurdal-l8a.no":true,"sor-fron.no":true,"xn--sr-fron-q1a.no":true,"sor-odal.no":true,"xn--sr-odal-q1a.no":true,"sor-varanger.no":true,"xn--sr-varanger-ggb.no":true,"matta-varjjat.no":true,"xn--mtta-vrjjat-k7af.no":true,"sorfold.no":true,"xn--srfold-bya.no":true,"sorreisa.no":true,"xn--srreisa-q1a.no":true,"sorum.no":true,"xn--srum-gra.no":true,"tana.no":true,"deatnu.no":true,"time.no":true,"tingvoll.no":true,"tinn.no":true,"tjeldsund.no":true,"dielddanuorri.no":true,"tjome.no":true,"xn--tjme-hra.no":true,"tokke.no":true,"tolga.no":true,"torsken.no":true,"tranoy.no":true,"xn--trany-yua.no":true,"tromso.no":true,"xn--troms-zua.no":true,"tromsa.no":true,"romsa.no":true,"trondheim.no":true,"troandin.no":true,"trysil.no":true,"trana.no":true,"xn--trna-woa.no":true,"trogstad.no":true,"xn--trgstad-r1a.no":true,"tvedestrand.no":true,"tydal.no":true,"tynset.no":true,"tysfjord.no":true,"divtasvuodna.no":true,"divttasvuotna.no":true,"tysnes.no":true,"tysvar.no":true,"xn--tysvr-vra.no":true,"tonsberg.no":true,"xn--tnsberg-q1a.no":true,"ullensaker.no":true,"ullensvang.no":true,"ulvik.no":true,"utsira.no":true,"vadso.no":true,"xn--vads-jra.no":true,"cahcesuolo.no":true,"xn--hcesuolo-7ya35b.no":true,"vaksdal.no":true,"valle.no":true,"vang.no":true,"vanylven.no":true,"vardo.no":true,"xn--vard-jra.no":true,"varggat.no":true,"xn--vrggt-xqad.no":true,"vefsn.no":true,"vaapste.no":true,"vega.no":true,"vegarshei.no":true,"xn--vegrshei-c0a.no":true,"vennesla.no":true,"verdal.no":true,"verran.no":true,"vestby.no":true,"vestnes.no":true,"vestre-slidre.no":true,"vestre-toten.no":true,"vestvagoy.no":true,"xn--vestvgy-ixa6o.no":true,"vevelstad.no":true,"vik.no":true,"vikna.no":true,"vindafjord.no":true,"volda.no":true,"voss.no":true,"varoy.no":true,"xn--vry-yla5g.no":true,"vagan.no":true,"xn--vgan-qoa.no":true,"voagat.no":true,"vagsoy.no":true,"xn--vgsy-qoa0j.no":true,"vaga.no":true,"xn--vg-yiab.no":true,"valer.ostfold.no":true,"xn--vler-qoa.xn--stfold-9xa.no":true,"valer.hedmark.no":true,"xn--vler-qoa.hedmark.no":true,"*.np":true,"nr":true,"biz.nr":true,"info.nr":true,"gov.nr":true,"edu.nr":true,"org.nr":true,"net.nr":true,"com.nr":true,"nu":true,"nz":true,"ac.nz":true,"co.nz":true,"cri.nz":true,"geek.nz":true,"gen.nz":true,"govt.nz":true,"health.nz":true,"iwi.nz":true,"kiwi.nz":true,"maori.nz":true,"mil.nz":true,"xn--mori-qsa.nz":true,"net.nz":true,"org.nz":true,"parliament.nz":true,"school.nz":true,"om":true,"co.om":true,"com.om":true,"edu.om":true,"gov.om":true,"med.om":true,"museum.om":true,"net.om":true,"org.om":true,"pro.om":true,"org":true,"pa":true,"ac.pa":true,"gob.pa":true,"com.pa":true,"org.pa":true,"sld.pa":true,"edu.pa":true,"net.pa":true,"ing.pa":true,"abo.pa":true,"med.pa":true,"nom.pa":true,"pe":true,"edu.pe":true,"gob.pe":true,"nom.pe":true,"mil.pe":true,"org.pe":true,"com.pe":true,"net.pe":true,"pf":true,"com.pf":true,"org.pf":true,"edu.pf":true,"*.pg":true,"ph":true,"com.ph":true,"net.ph":true,"org.ph":true,"gov.ph":true,"edu.ph":true,"ngo.ph":true,"mil.ph":true,"i.ph":true,"pk":true,"com.pk":true,"net.pk":true,"edu.pk":true,"org.pk":true,"fam.pk":true,"biz.pk":true,"web.pk":true,"gov.pk":true,"gob.pk":true,"gok.pk":true,"gon.pk":true,"gop.pk":true,"gos.pk":true,"info.pk":true,"pl":true,"com.pl":true,"net.pl":true,"org.pl":true,"info.pl":true,"waw.pl":true,"gov.pl":true,"aid.pl":true,"agro.pl":true,"atm.pl":true,"auto.pl":true,"biz.pl":true,"edu.pl":true,"gmina.pl":true,"gsm.pl":true,"mail.pl":true,"miasta.pl":true,"media.pl":true,"mil.pl":true,"nieruchomosci.pl":true,"nom.pl":true,"pc.pl":true,"powiat.pl":true,"priv.pl":true,"realestate.pl":true,"rel.pl":true,"sex.pl":true,"shop.pl":true,"sklep.pl":true,"sos.pl":true,"szkola.pl":true,"targi.pl":true,"tm.pl":true,"tourism.pl":true,"travel.pl":true,"turystyka.pl":true,"uw.gov.pl":true,"um.gov.pl":true,"ug.gov.pl":true,"upow.gov.pl":true,"starostwo.gov.pl":true,"so.gov.pl":true,"sr.gov.pl":true,"po.gov.pl":true,"pa.gov.pl":true,"augustow.pl":true,"babia-gora.pl":true,"bedzin.pl":true,"beskidy.pl":true,"bialowieza.pl":true,"bialystok.pl":true,"bielawa.pl":true,"bieszczady.pl":true,"boleslawiec.pl":true,"bydgoszcz.pl":true,"bytom.pl":true,"cieszyn.pl":true,"czeladz.pl":true,"czest.pl":true,"dlugoleka.pl":true,"elblag.pl":true,"elk.pl":true,"glogow.pl":true,"gniezno.pl":true,"gorlice.pl":true,"grajewo.pl":true,"ilawa.pl":true,"jaworzno.pl":true,"jelenia-gora.pl":true,"jgora.pl":true,"kalisz.pl":true,"kazimierz-dolny.pl":true,"karpacz.pl":true,"kartuzy.pl":true,"kaszuby.pl":true,"katowice.pl":true,"kepno.pl":true,"ketrzyn.pl":true,"klodzko.pl":true,"kobierzyce.pl":true,"kolobrzeg.pl":true,"konin.pl":true,"konskowola.pl":true,"kutno.pl":true,"lapy.pl":true,"lebork.pl":true,"legnica.pl":true,"lezajsk.pl":true,"limanowa.pl":true,"lomza.pl":true,"lowicz.pl":true,"lubin.pl":true,"lukow.pl":true,"malbork.pl":true,"malopolska.pl":true,"mazowsze.pl":true,"mazury.pl":true,"mielec.pl":true,"mielno.pl":true,"mragowo.pl":true,"naklo.pl":true,"nowaruda.pl":true,"nysa.pl":true,"olawa.pl":true,"olecko.pl":true,"olkusz.pl":true,"olsztyn.pl":true,"opoczno.pl":true,"opole.pl":true,"ostroda.pl":true,"ostroleka.pl":true,"ostrowiec.pl":true,"ostrowwlkp.pl":true,"pila.pl":true,"pisz.pl":true,"podhale.pl":true,"podlasie.pl":true,"polkowice.pl":true,"pomorze.pl":true,"pomorskie.pl":true,"prochowice.pl":true,"pruszkow.pl":true,"przeworsk.pl":true,"pulawy.pl":true,"radom.pl":true,"rawa-maz.pl":true,"rybnik.pl":true,"rzeszow.pl":true,"sanok.pl":true,"sejny.pl":true,"slask.pl":true,"slupsk.pl":true,"sosnowiec.pl":true,"stalowa-wola.pl":true,"skoczow.pl":true,"starachowice.pl":true,"stargard.pl":true,"suwalki.pl":true,"swidnica.pl":true,"swiebodzin.pl":true,"swinoujscie.pl":true,"szczecin.pl":true,"szczytno.pl":true,"tarnobrzeg.pl":true,"tgory.pl":true,"turek.pl":true,"tychy.pl":true,"ustka.pl":true,"walbrzych.pl":true,"warmia.pl":true,"warszawa.pl":true,"wegrow.pl":true,"wielun.pl":true,"wlocl.pl":true,"wloclawek.pl":true,"wodzislaw.pl":true,"wolomin.pl":true,"wroclaw.pl":true,"zachpomor.pl":true,"zagan.pl":true,"zarow.pl":true,"zgora.pl":true,"zgorzelec.pl":true,"pm":true,"pn":true,"gov.pn":true,"co.pn":true,"org.pn":true,"edu.pn":true,"net.pn":true,"post":true,"pr":true,"com.pr":true,"net.pr":true,"org.pr":true,"gov.pr":true,"edu.pr":true,"isla.pr":true,"pro.pr":true,"biz.pr":true,"info.pr":true,"name.pr":true,"est.pr":true,"prof.pr":true,"ac.pr":true,"pro":true,"aca.pro":true,"bar.pro":true,"cpa.pro":true,"jur.pro":true,"law.pro":true,"med.pro":true,"eng.pro":true,"ps":true,"edu.ps":true,"gov.ps":true,"sec.ps":true,"plo.ps":true,"com.ps":true,"org.ps":true,"net.ps":true,"pt":true,"net.pt":true,"gov.pt":true,"org.pt":true,"edu.pt":true,"int.pt":true,"publ.pt":true,"com.pt":true,"nome.pt":true,"pw":true,"co.pw":true,"ne.pw":true,"or.pw":true,"ed.pw":true,"go.pw":true,"belau.pw":true,"py":true,"com.py":true,"coop.py":true,"edu.py":true,"gov.py":true,"mil.py":true,"net.py":true,"org.py":true,"qa":true,"com.qa":true,"edu.qa":true,"gov.qa":true,"mil.qa":true,"name.qa":true,"net.qa":true,"org.qa":true,"sch.qa":true,"re":true,"com.re":true,"asso.re":true,"nom.re":true,"ro":true,"com.ro":true,"org.ro":true,"tm.ro":true,"nt.ro":true,"nom.ro":true,"info.ro":true,"rec.ro":true,"arts.ro":true,"firm.ro":true,"store.ro":true,"www.ro":true,"rs":true,"co.rs":true,"org.rs":true,"edu.rs":true,"ac.rs":true,"gov.rs":true,"in.rs":true,"ru":true,"ac.ru":true,"com.ru":true,"edu.ru":true,"int.ru":true,"net.ru":true,"org.ru":true,"pp.ru":true,"adygeya.ru":true,"altai.ru":true,"amur.ru":true,"arkhangelsk.ru":true,"astrakhan.ru":true,"bashkiria.ru":true,"belgorod.ru":true,"bir.ru":true,"bryansk.ru":true,"buryatia.ru":true,"cbg.ru":true,"chel.ru":true,"chelyabinsk.ru":true,"chita.ru":true,"chukotka.ru":true,"chuvashia.ru":true,"dagestan.ru":true,"dudinka.ru":true,"e-burg.ru":true,"grozny.ru":true,"irkutsk.ru":true,"ivanovo.ru":true,"izhevsk.ru":true,"jar.ru":true,"joshkar-ola.ru":true,"kalmykia.ru":true,"kaluga.ru":true,"kamchatka.ru":true,"karelia.ru":true,"kazan.ru":true,"kchr.ru":true,"kemerovo.ru":true,"khabarovsk.ru":true,"khakassia.ru":true,"khv.ru":true,"kirov.ru":true,"koenig.ru":true,"komi.ru":true,"kostroma.ru":true,"krasnoyarsk.ru":true,"kuban.ru":true,"kurgan.ru":true,"kursk.ru":true,"lipetsk.ru":true,"magadan.ru":true,"mari.ru":true,"mari-el.ru":true,"marine.ru":true,"mordovia.ru":true,"msk.ru":true,"murmansk.ru":true,"nalchik.ru":true,"nnov.ru":true,"nov.ru":true,"novosibirsk.ru":true,"nsk.ru":true,"omsk.ru":true,"orenburg.ru":true,"oryol.ru":true,"palana.ru":true,"penza.ru":true,"perm.ru":true,"ptz.ru":true,"rnd.ru":true,"ryazan.ru":true,"sakhalin.ru":true,"samara.ru":true,"saratov.ru":true,"simbirsk.ru":true,"smolensk.ru":true,"spb.ru":true,"stavropol.ru":true,"stv.ru":true,"surgut.ru":true,"tambov.ru":true,"tatarstan.ru":true,"tom.ru":true,"tomsk.ru":true,"tsaritsyn.ru":true,"tsk.ru":true,"tula.ru":true,"tuva.ru":true,"tver.ru":true,"tyumen.ru":true,"udm.ru":true,"udmurtia.ru":true,"ulan-ude.ru":true,"vladikavkaz.ru":true,"vladimir.ru":true,"vladivostok.ru":true,"volgograd.ru":true,"vologda.ru":true,"voronezh.ru":true,"vrn.ru":true,"vyatka.ru":true,"yakutia.ru":true,"yamal.ru":true,"yaroslavl.ru":true,"yekaterinburg.ru":true,"yuzhno-sakhalinsk.ru":true,"amursk.ru":true,"baikal.ru":true,"cmw.ru":true,"fareast.ru":true,"jamal.ru":true,"kms.ru":true,"k-uralsk.ru":true,"kustanai.ru":true,"kuzbass.ru":true,"magnitka.ru":true,"mytis.ru":true,"nakhodka.ru":true,"nkz.ru":true,"norilsk.ru":true,"oskol.ru":true,"pyatigorsk.ru":true,"rubtsovsk.ru":true,"snz.ru":true,"syzran.ru":true,"vdonsk.ru":true,"zgrad.ru":true,"gov.ru":true,"mil.ru":true,"test.ru":true,"rw":true,"gov.rw":true,"net.rw":true,"edu.rw":true,"ac.rw":true,"com.rw":true,"co.rw":true,"int.rw":true,"mil.rw":true,"gouv.rw":true,"sa":true,"com.sa":true,"net.sa":true,"org.sa":true,"gov.sa":true,"med.sa":true,"pub.sa":true,"edu.sa":true,"sch.sa":true,"sb":true,"com.sb":true,"edu.sb":true,"gov.sb":true,"net.sb":true,"org.sb":true,"sc":true,"com.sc":true,"gov.sc":true,"net.sc":true,"org.sc":true,"edu.sc":true,"sd":true,"com.sd":true,"net.sd":true,"org.sd":true,"edu.sd":true,"med.sd":true,"tv.sd":true,"gov.sd":true,"info.sd":true,"se":true,"a.se":true,"ac.se":true,"b.se":true,"bd.se":true,"brand.se":true,"c.se":true,"d.se":true,"e.se":true,"f.se":true,"fh.se":true,"fhsk.se":true,"fhv.se":true,"g.se":true,"h.se":true,"i.se":true,"k.se":true,"komforb.se":true,"kommunalforbund.se":true,"komvux.se":true,"l.se":true,"lanbib.se":true,"m.se":true,"n.se":true,"naturbruksgymn.se":true,"o.se":true,"org.se":true,"p.se":true,"parti.se":true,"pp.se":true,"press.se":true,"r.se":true,"s.se":true,"t.se":true,"tm.se":true,"u.se":true,"w.se":true,"x.se":true,"y.se":true,"z.se":true,"sg":true,"com.sg":true,"net.sg":true,"org.sg":true,"gov.sg":true,"edu.sg":true,"per.sg":true,"sh":true,"com.sh":true,"net.sh":true,"gov.sh":true,"org.sh":true,"mil.sh":true,"si":true,"sj":true,"sk":true,"sl":true,"com.sl":true,"net.sl":true,"edu.sl":true,"gov.sl":true,"org.sl":true,"sm":true,"sn":true,"art.sn":true,"com.sn":true,"edu.sn":true,"gouv.sn":true,"org.sn":true,"perso.sn":true,"univ.sn":true,"so":true,"com.so":true,"net.so":true,"org.so":true,"sr":true,"st":true,"co.st":true,"com.st":true,"consulado.st":true,"edu.st":true,"embaixada.st":true,"gov.st":true,"mil.st":true,"net.st":true,"org.st":true,"principe.st":true,"saotome.st":true,"store.st":true,"su":true,"adygeya.su":true,"arkhangelsk.su":true,"balashov.su":true,"bashkiria.su":true,"bryansk.su":true,"dagestan.su":true,"grozny.su":true,"ivanovo.su":true,"kalmykia.su":true,"kaluga.su":true,"karelia.su":true,"khakassia.su":true,"krasnodar.su":true,"kurgan.su":true,"lenug.su":true,"mordovia.su":true,"msk.su":true,"murmansk.su":true,"nalchik.su":true,"nov.su":true,"obninsk.su":true,"penza.su":true,"pokrovsk.su":true,"sochi.su":true,"spb.su":true,"togliatti.su":true,"troitsk.su":true,"tula.su":true,"tuva.su":true,"vladikavkaz.su":true,"vladimir.su":true,"vologda.su":true,"sv":true,"com.sv":true,"edu.sv":true,"gob.sv":true,"org.sv":true,"red.sv":true,"sx":true,"gov.sx":true,"sy":true,"edu.sy":true,"gov.sy":true,"net.sy":true,"mil.sy":true,"com.sy":true,"org.sy":true,"sz":true,"co.sz":true,"ac.sz":true,"org.sz":true,"tc":true,"td":true,"tel":true,"tf":true,"tg":true,"th":true,"ac.th":true,"co.th":true,"go.th":true,"in.th":true,"mi.th":true,"net.th":true,"or.th":true,"tj":true,"ac.tj":true,"biz.tj":true,"co.tj":true,"com.tj":true,"edu.tj":true,"go.tj":true,"gov.tj":true,"int.tj":true,"mil.tj":true,"name.tj":true,"net.tj":true,"nic.tj":true,"org.tj":true,"test.tj":true,"web.tj":true,"tk":true,"tl":true,"gov.tl":true,"tm":true,"com.tm":true,"co.tm":true,"org.tm":true,"net.tm":true,"nom.tm":true,"gov.tm":true,"mil.tm":true,"edu.tm":true,"tn":true,"com.tn":true,"ens.tn":true,"fin.tn":true,"gov.tn":true,"ind.tn":true,"intl.tn":true,"nat.tn":true,"net.tn":true,"org.tn":true,"info.tn":true,"perso.tn":true,"tourism.tn":true,"edunet.tn":true,"rnrt.tn":true,"rns.tn":true,"rnu.tn":true,"mincom.tn":true,"agrinet.tn":true,"defense.tn":true,"turen.tn":true,"to":true,"com.to":true,"gov.to":true,"net.to":true,"org.to":true,"edu.to":true,"mil.to":true,"tp":true,"tr":true,"com.tr":true,"info.tr":true,"biz.tr":true,"net.tr":true,"org.tr":true,"web.tr":true,"gen.tr":true,"tv.tr":true,"av.tr":true,"dr.tr":true,"bbs.tr":true,"name.tr":true,"tel.tr":true,"gov.tr":true,"bel.tr":true,"pol.tr":true,"mil.tr":true,"k12.tr":true,"edu.tr":true,"kep.tr":true,"nc.tr":true,"gov.nc.tr":true,"travel":true,"tt":true,"co.tt":true,"com.tt":true,"org.tt":true,"net.tt":true,"biz.tt":true,"info.tt":true,"pro.tt":true,"int.tt":true,"coop.tt":true,"jobs.tt":true,"mobi.tt":true,"travel.tt":true,"museum.tt":true,"aero.tt":true,"name.tt":true,"gov.tt":true,"edu.tt":true,"tv":true,"tw":true,"edu.tw":true,"gov.tw":true,"mil.tw":true,"com.tw":true,"net.tw":true,"org.tw":true,"idv.tw":true,"game.tw":true,"ebiz.tw":true,"club.tw":true,"xn--zf0ao64a.tw":true,"xn--uc0atv.tw":true,"xn--czrw28b.tw":true,"tz":true,"ac.tz":true,"co.tz":true,"go.tz":true,"hotel.tz":true,"info.tz":true,"me.tz":true,"mil.tz":true,"mobi.tz":true,"ne.tz":true,"or.tz":true,"sc.tz":true,"tv.tz":true,"ua":true,"com.ua":true,"edu.ua":true,"gov.ua":true,"in.ua":true,"net.ua":true,"org.ua":true,"cherkassy.ua":true,"cherkasy.ua":true,"chernigov.ua":true,"chernihiv.ua":true,"chernivtsi.ua":true,"chernovtsy.ua":true,"ck.ua":true,"cn.ua":true,"cr.ua":true,"crimea.ua":true,"cv.ua":true,"dn.ua":true,"dnepropetrovsk.ua":true,"dnipropetrovsk.ua":true,"dominic.ua":true,"donetsk.ua":true,"dp.ua":true,"if.ua":true,"ivano-frankivsk.ua":true,"kh.ua":true,"kharkiv.ua":true,"kharkov.ua":true,"kherson.ua":true,"khmelnitskiy.ua":true,"khmelnytskyi.ua":true,"kiev.ua":true,"kirovograd.ua":true,"km.ua":true,"kr.ua":true,"krym.ua":true,"ks.ua":true,"kv.ua":true,"kyiv.ua":true,"lg.ua":true,"lt.ua":true,"lugansk.ua":true,"lutsk.ua":true,"lv.ua":true,"lviv.ua":true,"mk.ua":true,"mykolaiv.ua":true,"nikolaev.ua":true,"od.ua":true,"odesa.ua":true,"odessa.ua":true,"pl.ua":true,"poltava.ua":true,"rivne.ua":true,"rovno.ua":true,"rv.ua":true,"sb.ua":true,"sebastopol.ua":true,"sevastopol.ua":true,"sm.ua":true,"sumy.ua":true,"te.ua":true,"ternopil.ua":true,"uz.ua":true,"uzhgorod.ua":true,"vinnica.ua":true,"vinnytsia.ua":true,"vn.ua":true,"volyn.ua":true,"yalta.ua":true,"zaporizhzhe.ua":true,"zaporizhzhia.ua":true,"zhitomir.ua":true,"zhytomyr.ua":true,"zp.ua":true,"zt.ua":true,"co.ua":true,"pp.ua":true,"ug":true,"co.ug":true,"or.ug":true,"ac.ug":true,"sc.ug":true,"go.ug":true,"ne.ug":true,"com.ug":true,"org.ug":true,"uk":true,"ac.uk":true,"co.uk":true,"gov.uk":true,"ltd.uk":true,"me.uk":true,"net.uk":true,"nhs.uk":true,"org.uk":true,"plc.uk":true,"police.uk":true,"*.sch.uk":true,"us":true,"dni.us":true,"fed.us":true,"isa.us":true,"kids.us":true,"nsn.us":true,"ak.us":true,"al.us":true,"ar.us":true,"as.us":true,"az.us":true,"ca.us":true,"co.us":true,"ct.us":true,"dc.us":true,"de.us":true,"fl.us":true,"ga.us":true,"gu.us":true,"hi.us":true,"ia.us":true,"id.us":true,"il.us":true,"in.us":true,"ks.us":true,"ky.us":true,"la.us":true,"ma.us":true,"md.us":true,"me.us":true,"mi.us":true,"mn.us":true,"mo.us":true,"ms.us":true,"mt.us":true,"nc.us":true,"nd.us":true,"ne.us":true,"nh.us":true,"nj.us":true,"nm.us":true,"nv.us":true,"ny.us":true,"oh.us":true,"ok.us":true,"or.us":true,"pa.us":true,"pr.us":true,"ri.us":true,"sc.us":true,"sd.us":true,"tn.us":true,"tx.us":true,"ut.us":true,"vi.us":true,"vt.us":true,"va.us":true,"wa.us":true,"wi.us":true,"wv.us":true,"wy.us":true,"k12.ak.us":true,"k12.al.us":true,"k12.ar.us":true,"k12.as.us":true,"k12.az.us":true,"k12.ca.us":true,"k12.co.us":true,"k12.ct.us":true,"k12.dc.us":true,"k12.de.us":true,"k12.fl.us":true,"k12.ga.us":true,"k12.gu.us":true,"k12.ia.us":true,"k12.id.us":true,"k12.il.us":true,"k12.in.us":true,"k12.ks.us":true,"k12.ky.us":true,"k12.la.us":true,"k12.ma.us":true,"k12.md.us":true,"k12.me.us":true,"k12.mi.us":true,"k12.mn.us":true,"k12.mo.us":true,"k12.ms.us":true,"k12.mt.us":true,"k12.nc.us":true,"k12.ne.us":true,"k12.nh.us":true,"k12.nj.us":true,"k12.nm.us":true,"k12.nv.us":true,"k12.ny.us":true,"k12.oh.us":true,"k12.ok.us":true,"k12.or.us":true,"k12.pa.us":true,"k12.pr.us":true,"k12.ri.us":true,"k12.sc.us":true,"k12.tn.us":true,"k12.tx.us":true,"k12.ut.us":true,"k12.vi.us":true,"k12.vt.us":true,"k12.va.us":true,"k12.wa.us":true,"k12.wi.us":true,"k12.wy.us":true,"cc.ak.us":true,"cc.al.us":true,"cc.ar.us":true,"cc.as.us":true,"cc.az.us":true,"cc.ca.us":true,"cc.co.us":true,"cc.ct.us":true,"cc.dc.us":true,"cc.de.us":true,"cc.fl.us":true,"cc.ga.us":true,"cc.gu.us":true,"cc.hi.us":true,"cc.ia.us":true,"cc.id.us":true,"cc.il.us":true,"cc.in.us":true,"cc.ks.us":true,"cc.ky.us":true,"cc.la.us":true,"cc.ma.us":true,"cc.md.us":true,"cc.me.us":true,"cc.mi.us":true,"cc.mn.us":true,"cc.mo.us":true,"cc.ms.us":true,"cc.mt.us":true,"cc.nc.us":true,"cc.nd.us":true,"cc.ne.us":true,"cc.nh.us":true,"cc.nj.us":true,"cc.nm.us":true,"cc.nv.us":true,"cc.ny.us":true,"cc.oh.us":true,"cc.ok.us":true,"cc.or.us":true,"cc.pa.us":true,"cc.pr.us":true,"cc.ri.us":true,"cc.sc.us":true,"cc.sd.us":true,"cc.tn.us":true,"cc.tx.us":true,"cc.ut.us":true,"cc.vi.us":true,"cc.vt.us":true,"cc.va.us":true,"cc.wa.us":true,"cc.wi.us":true,"cc.wv.us":true,"cc.wy.us":true,"lib.ak.us":true,"lib.al.us":true,"lib.ar.us":true,"lib.as.us":true,"lib.az.us":true,"lib.ca.us":true,"lib.co.us":true,"lib.ct.us":true,"lib.dc.us":true,"lib.de.us":true,"lib.fl.us":true,"lib.ga.us":true,"lib.gu.us":true,"lib.hi.us":true,"lib.ia.us":true,"lib.id.us":true,"lib.il.us":true,"lib.in.us":true,"lib.ks.us":true,"lib.ky.us":true,"lib.la.us":true,"lib.ma.us":true,"lib.md.us":true,"lib.me.us":true,"lib.mi.us":true,"lib.mn.us":true,"lib.mo.us":true,"lib.ms.us":true,"lib.mt.us":true,"lib.nc.us":true,"lib.nd.us":true,"lib.ne.us":true,"lib.nh.us":true,"lib.nj.us":true,"lib.nm.us":true,"lib.nv.us":true,"lib.ny.us":true,"lib.oh.us":true,"lib.ok.us":true,"lib.or.us":true,"lib.pa.us":true,"lib.pr.us":true,"lib.ri.us":true,"lib.sc.us":true,"lib.sd.us":true,"lib.tn.us":true,"lib.tx.us":true,"lib.ut.us":true,"lib.vi.us":true,"lib.vt.us":true,"lib.va.us":true,"lib.wa.us":true,"lib.wi.us":true,"lib.wy.us":true,"pvt.k12.ma.us":true,"chtr.k12.ma.us":true,"paroch.k12.ma.us":true,"uy":true,"com.uy":true,"edu.uy":true,"gub.uy":true,"mil.uy":true,"net.uy":true,"org.uy":true,"uz":true,"co.uz":true,"com.uz":true,"net.uz":true,"org.uz":true,"va":true,"vc":true,"com.vc":true,"net.vc":true,"org.vc":true,"gov.vc":true,"mil.vc":true,"edu.vc":true,"ve":true,"arts.ve":true,"co.ve":true,"com.ve":true,"e12.ve":true,"edu.ve":true,"firm.ve":true,"gob.ve":true,"gov.ve":true,"info.ve":true,"int.ve":true,"mil.ve":true,"net.ve":true,"org.ve":true,"rec.ve":true,"store.ve":true,"tec.ve":true,"web.ve":true,"vg":true,"vi":true,"co.vi":true,"com.vi":true,"k12.vi":true,"net.vi":true,"org.vi":true,"vn":true,"com.vn":true,"net.vn":true,"org.vn":true,"edu.vn":true,"gov.vn":true,"int.vn":true,"ac.vn":true,"biz.vn":true,"info.vn":true,"name.vn":true,"pro.vn":true,"health.vn":true,"vu":true,"com.vu":true,"edu.vu":true,"net.vu":true,"org.vu":true,"wf":true,"ws":true,"com.ws":true,"net.ws":true,"org.ws":true,"gov.ws":true,"edu.ws":true,"yt":true,"xn--mgbaam7a8h":true,"xn--54b7fta0cc":true,"xn--fiqs8s":true,"xn--fiqz9s":true,"xn--lgbbat1ad8j":true,"xn--wgbh1c":true,"xn--node":true,"xn--j6w193g":true,"xn--h2brj9c":true,"xn--mgbbh1a71e":true,"xn--fpcrj9c3d":true,"xn--gecrj9c":true,"xn--s9brj9c":true,"xn--45brj9c":true,"xn--xkc2dl3a5ee0h":true,"xn--mgba3a4f16a":true,"xn--mgba3a4fra":true,"xn--mgbayh7gpa":true,"xn--3e0b707e":true,"xn--80ao21a":true,"xn--fzc2c9e2c":true,"xn--xkc2al3hye2a":true,"xn--mgbc0a9azcg":true,"xn--l1acc":true,"xn--mgbx4cd0ab":true,"xn--mgb9awbf":true,"xn--ygbi2ammx":true,"xn--90a3ac":true,"xn--o1ac.xn--90a3ac":true,"xn--c1avg.xn--90a3ac":true,"xn--90azh.xn--90a3ac":true,"xn--d1at.xn--90a3ac":true,"xn--o1ach.xn--90a3ac":true,"xn--80au.xn--90a3ac":true,"xn--p1ai":true,"xn--wgbl6a":true,"xn--mgberp4a5d4ar":true,"xn--mgberp4a5d4a87g":true,"xn--mgbqly7c0a67fbc":true,"xn--mgbqly7cvafr":true,"xn--ogbpf8fl":true,"xn--mgbtf8fl":true,"xn--yfro4i67o":true,"xn--clchc0ea0b2g2a9gcd":true,"xn--o3cw4h":true,"xn--pgbs0dh":true,"xn--kpry57d":true,"xn--kprw13d":true,"xn--nnx388a":true,"xn--j1amh":true,"xn--mgb2ddes":true,"xxx":true,"*.ye":true,"*.za":true,"*.zm":true,"*.zw":true,"aaa":true,"abb":true,"abbott":true,"abogado":true,"academy":true,"accenture":true,"accountant":true,"accountants":true,"aco":true,"active":true,"actor":true,"ads":true,"adult":true,"aeg":true,"afl":true,"africa":true,"africamagic":true,"agency":true,"aig":true,"airforce":true,"airtel":true,"alibaba":true,"alipay":true,"allfinanz":true,"alsace":true,"amsterdam":true,"analytics":true,"android":true,"anquan":true,"apartments":true,"aquarelle":true,"aramco":true,"archi":true,"army":true,"arte":true,"associates":true,"attorney":true,"auction":true,"audio":true,"author":true,"auto":true,"autos":true,"avianca":true,"axa":true,"azure":true,"baidu":true,"band":true,"bank":true,"bar":true,"barcelona":true,"barclaycard":true,"barclays":true,"bargains":true,"bauhaus":true,"bayern":true,"bbc":true,"bbva":true,"bcg":true,"bcn":true,"beer":true,"bentley":true,"berlin":true,"best":true,"bharti":true,"bible":true,"bid":true,"bike":true,"bing":true,"bingo":true,"bio":true,"black":true,"blackfriday":true,"bloomberg":true,"blue":true,"bms":true,"bmw":true,"bnl":true,"bnpparibas":true,"boats":true,"bom":true,"bond":true,"boo":true,"boots":true,"bot":true,"boutique":true,"bradesco":true,"bridgestone":true,"broadway":true,"broker":true,"brother":true,"brussels":true,"budapest":true,"build":true,"builders":true,"business":true,"buy":true,"buzz":true,"bzh":true,"cab":true,"cafe":true,"cal":true,"call":true,"camera":true,"camp":true,"cancerresearch":true,"canon":true,"capetown":true,"capital":true,"car":true,"caravan":true,"cards":true,"care":true,"career":true,"careers":true,"cars":true,"cartier":true,"casa":true,"cash":true,"casino":true,"catering":true,"cba":true,"cbn":true,"center":true,"ceo":true,"cern":true,"cfa":true,"cfd":true,"channel":true,"chat":true,"cheap":true,"chloe":true,"christmas":true,"chrome":true,"church":true,"cipriani":true,"circle":true,"cisco":true,"citic":true,"city":true,"cityeats":true,"claims":true,"cleaning":true,"click":true,"clinic":true,"clothing":true,"club":true,"coach":true,"codes":true,"coffee":true,"college":true,"cologne":true,"commbank":true,"community":true,"company":true,"computer":true,"comsec":true,"condos":true,"construction":true,"consulting":true,"contact":true,"contractors":true,"cooking":true,"cool":true,"corsica":true,"country":true,"coupon":true,"coupons":true,"courses":true,"credit":true,"creditcard":true,"creditunion":true,"cricket":true,"crown":true,"crs":true,"cruises":true,"csc":true,"cuisinella":true,"cymru":true,"cyou":true,"dabur":true,"dad":true,"dance":true,"date":true,"dating":true,"datsun":true,"day":true,"dclk":true,"dealer":true,"deals":true,"degree":true,"delivery":true,"dell":true,"delta":true,"democrat":true,"dental":true,"dentist":true,"desi":true,"design":true,"dev":true,"diamonds":true,"diet":true,"digital":true,"direct":true,"directory":true,"discount":true,"dnp":true,"docs":true,"dog":true,"doha":true,"domains":true,"doosan":true,"download":true,"drive":true,"dstv":true,"dubai":true,"durban":true,"dvag":true,"earth":true,"eat":true,"edeka":true,"education":true,"email":true,"emerck":true,"energy":true,"engineer":true,"engineering":true,"enterprises":true,"epson":true,"equipment":true,"erni":true,"esq":true,"estate":true,"eurovision":true,"eus":true,"events":true,"everbank":true,"exchange":true,"expert":true,"exposed":true,"express":true,"fage":true,"fail":true,"fairwinds":true,"faith":true,"family":true,"fan":true,"fans":true,"farm":true,"fashion":true,"fast":true,"feedback":true,"ferrero":true,"film":true,"final":true,"finance":true,"financial":true,"firestone":true,"firmdale":true,"fish":true,"fishing":true,"fit":true,"fitness":true,"flickr":true,"flights":true,"florist":true,"flowers":true,"flsmidth":true,"fly":true,"foo":true,"football":true,"ford":true,"forex":true,"forsale":true,"forum":true,"foundation":true,"frl":true,"frogans":true,"frontier":true,"fund":true,"furniture":true,"futbol":true,"fyi":true,"gal":true,"gallery":true,"gallup":true,"garden":true,"gbiz":true,"gdn":true,"gea":true,"gent":true,"genting":true,"ggee":true,"gift":true,"gifts":true,"gives":true,"giving":true,"glass":true,"gle":true,"global":true,"globo":true,"gmail":true,"gmo":true,"gmx":true,"gold":true,"goldpoint":true,"golf":true,"goo":true,"goog":true,"google":true,"gop":true,"got":true,"gotv":true,"graphics":true,"gratis":true,"green":true,"gripe":true,"group":true,"gucci":true,"guge":true,"guide":true,"guitars":true,"guru":true,"hamburg":true,"hangout":true,"haus":true,"hdfcbank":true,"health":true,"healthcare":true,"help":true,"helsinki":true,"here":true,"hermes":true,"hiphop":true,"hitachi":true,"hiv":true,"hockey":true,"holdings":true,"holiday":true,"homedepot":true,"homes":true,"honda":true,"horse":true,"host":true,"hosting":true,"hoteles":true,"hotmail":true,"house":true,"how":true,"hsbc":true,"htc":true,"ibm":true,"icbc":true,"ice":true,"icu":true,"ifm":true,"iinet":true,"immo":true,"immobilien":true,"industries":true,"infiniti":true,"ing":true,"ink":true,"institute":true,"insurance":true,"insure":true,"international":true,"investments":true,"ipiranga":true,"irish":true,"iselect":true,"ist":true,"istanbul":true,"itau":true,"iwc":true,"jaguar":true,"java":true,"jcb":true,"jetzt":true,"jewelry":true,"jio":true,"jlc":true,"jll":true,"jmp":true,"joburg":true,"jot":true,"joy":true,"jprs":true,"juegos":true,"kaufen":true,"kddi":true,"kfh":true,"kim":true,"kinder":true,"kitchen":true,"kiwi":true,"koeln":true,"komatsu":true,"kpn":true,"krd":true,"kred":true,"kyknet":true,"kyoto":true,"lacaixa":true,"lancaster":true,"land":true,"landrover":true,"lasalle":true,"lat":true,"latrobe":true,"law":true,"lawyer":true,"lds":true,"lease":true,"leclerc":true,"legal":true,"lgbt":true,"liaison":true,"lidl":true,"life":true,"lifeinsurance":true,"lifestyle":true,"lighting":true,"like":true,"limited":true,"limo":true,"lincoln":true,"linde":true,"link":true,"live":true,"lixil":true,"loan":true,"loans":true,"lol":true,"london":true,"lotte":true,"lotto":true,"love":true,"ltd":true,"ltda":true,"lupin":true,"luxe":true,"luxury":true,"madrid":true,"maif":true,"maison":true,"makeup":true,"man":true,"management":true,"mango":true,"market":true,"marketing":true,"markets":true,"marriott":true,"mba":true,"media":true,"meet":true,"melbourne":true,"meme":true,"memorial":true,"men":true,"menu":true,"meo":true,"miami":true,"microsoft":true,"mini":true,"mma":true,"mnet":true,"mobily":true,"moda":true,"moe":true,"moi":true,"monash":true,"money":true,"montblanc":true,"mormon":true,"mortgage":true,"moscow":true,"motorcycles":true,"mov":true,"movie":true,"movistar":true,"mtn":true,"mtpc":true,"mtr":true,"multichoice":true,"mutual":true,"mzansimagic":true,"nadex":true,"nagoya":true,"naspers":true,"natura":true,"navy":true,"nec":true,"netbank":true,"network":true,"neustar":true,"new":true,"news":true,"nexus":true,"ngo":true,"nhk":true,"nico":true,"ninja":true,"nissan":true,"nokia":true,"norton":true,"nowruz":true,"nra":true,"nrw":true,"ntt":true,"nyc":true,"obi":true,"office":true,"okinawa":true,"omega":true,"one":true,"ong":true,"onl":true,"online":true,"ooo":true,"oracle":true,"orange":true,"organic":true,"orientexpress":true,"osaka":true,"otsuka":true,"ovh":true,"page":true,"pamperedchef":true,"panerai":true,"paris":true,"pars":true,"partners":true,"parts":true,"party":true,"passagens":true,"payu":true,"pharmacy":true,"philips":true,"photo":true,"photography":true,"photos":true,"physio":true,"piaget":true,"pics":true,"pictet":true,"pictures":true,"pid":true,"pin":true,"pink":true,"pizza":true,"place":true,"play":true,"plumbing":true,"plus":true,"pohl":true,"poker":true,"porn":true,"praxi":true,"press":true,"prod":true,"productions":true,"prof":true,"promo":true,"properties":true,"property":true,"pub":true,"qpon":true,"quebec":true,"quest":true,"racing":true,"read":true,"realtor":true,"realty":true,"recipes":true,"red":true,"redstone":true,"redumbrella":true,"rehab":true,"reise":true,"reisen":true,"reit":true,"reliance":true,"ren":true,"rent":true,"rentals":true,"repair":true,"report":true,"republican":true,"rest":true,"restaurant":true,"review":true,"reviews":true,"rich":true,"ricoh":true,"ril":true,"rio":true,"rip":true,"rocher":true,"rocks":true,"rodeo":true,"room":true,"rsvp":true,"ruhr":true,"run":true,"rwe":true,"ryukyu":true,"saarland":true,"safe":true,"safety":true,"sakura":true,"sale":true,"salon":true,"samsung":true,"sandvik":true,"sandvikcoromant":true,"sanofi":true,"sap":true,"sapo":true,"sarl":true,"sas":true,"saxo":true,"sbi":true,"sbs":true,"sca":true,"scb":true,"schmidt":true,"scholarships":true,"school":true,"schule":true,"schwarz":true,"science":true,"scor":true,"scot":true,"seat":true,"seek":true,"sener":true,"services":true,"sew":true,"sex":true,"sexy":true,"sharp":true,"shia":true,"shiksha":true,"shoes":true,"shouji":true,"show":true,"shriram":true,"sina":true,"singles":true,"site":true,"skin":true,"sky":true,"skype":true,"smile":true,"sncf":true,"soccer":true,"social":true,"software":true,"sohu":true,"solar":true,"solutions":true,"song":true,"sony":true,"soy":true,"space":true,"spiegel":true,"spot":true,"spreadbetting":true,"stada":true,"star":true,"starhub":true,"statebank":true,"statoil":true,"stc":true,"stcgroup":true,"stockholm":true,"storage":true,"studio":true,"study":true,"style":true,"sucks":true,"supersport":true,"supplies":true,"supply":true,"support":true,"surf":true,"surgery":true,"suzuki":true,"swatch":true,"swiss":true,"sydney":true,"symantec":true,"systems":true,"tab":true,"taipei":true,"taobao":true,"tatamotors":true,"tatar":true,"tattoo":true,"tax":true,"taxi":true,"tci":true,"team":true,"tech":true,"technology":true,"telecity":true,"telefonica":true,"temasek":true,"tennis":true,"thd":true,"theater":true,"tickets":true,"tienda":true,"tiffany":true,"tips":true,"tires":true,"tirol":true,"tmall":true,"today":true,"tokyo":true,"tools":true,"top":true,"toray":true,"toshiba":true,"tours":true,"town":true,"toys":true,"trade":true,"trading":true,"training":true,"travelers":true,"travelersinsurance":true,"trust":true,"trv":true,"tui":true,"tunes":true,"tushu":true,"tvs":true,"ubs":true,"university":true,"uno":true,"uol":true,"vacations":true,"vana":true,"vegas":true,"ventures":true,"versicherung":true,"vet":true,"viajes":true,"video":true,"viking":true,"villas":true,"vip":true,"virgin":true,"vision":true,"vista":true,"vistaprint":true,"viva":true,"vlaanderen":true,"vodka":true,"vote":true,"voting":true,"voto":true,"voyage":true,"vuelos":true,"wales":true,"walter":true,"wang":true,"wanggou":true,"watch":true,"watches":true,"weather":true,"weatherchannel":true,"webcam":true,"website":true,"wed":true,"wedding":true,"weibo":true,"weir":true,"whoswho":true,"wien":true,"wiki":true,"williamhill":true,"win":true,"windows":true,"wme":true,"work":true,"works":true,"world":true,"wtc":true,"wtf":true,"xbox":true,"xerox":true,"xihuan":true,"xin":true,"xn--11b4c3d":true,"xn--1ck2e1b":true,"xn--1qqw23a":true,"xn--30rr7y":true,"xn--3bst00m":true,"xn--3ds443g":true,"xn--3pxu8k":true,"xn--42c2d9a":true,"xn--45q11c":true,"xn--4gbrim":true,"xn--55qw42g":true,"xn--55qx5d":true,"xn--5tzm5g":true,"xn--6frz82g":true,"xn--6qq986b3xl":true,"xn--80adxhks":true,"xn--80asehdb":true,"xn--80aswg":true,"xn--8y0a063a":true,"xn--9dbq2a":true,"xn--9et52u":true,"xn--9krt00a":true,"xn--b4w605ferd":true,"xn--bck1b9a5dre4c":true,"xn--c1avg":true,"xn--c2br7g":true,"xn--cck2b3b":true,"xn--cg4bki":true,"xn--czr694b":true,"xn--czrs0t":true,"xn--czru2d":true,"xn--d1acj3b":true,"xn--eckvdtc9d":true,"xn--efvy88h":true,"xn--estv75g":true,"xn--fhbei":true,"xn--fiq228c5hs":true,"xn--fiq64b":true,"xn--fjq720a":true,"xn--flw351e":true,"xn--g2xx48c":true,"xn--gckr3f0f":true,"xn--hxt814e":true,"xn--i1b6b1a6a2e":true,"xn--imr513n":true,"xn--io0a7i":true,"xn--j1aef":true,"xn--jlq61u9w7b":true,"xn--jvr189m":true,"xn--kcrx77d1x4a":true,"xn--kpu716f":true,"xn--kput3i":true,"xn--mgba3a3ejt":true,"xn--mgbab2bd":true,"xn--mgbb9fbpob":true,"xn--mgbt3dhd":true,"xn--mk1bu44c":true,"xn--mxtq1m":true,"xn--ngbc5azd":true,"xn--ngbe9e0a":true,"xn--nqv7f":true,"xn--nqv7fs00ema":true,"xn--nyqy26a":true,"xn--p1acf":true,"xn--pbt977c":true,"xn--pssy2u":true,"xn--q9jyb4c":true,"xn--qcka1pmc":true,"xn--rhqv96g":true,"xn--rovu88b":true,"xn--ses554g":true,"xn--t60b56a":true,"xn--tckwe":true,"xn--unup4y":true,"xn--vermgensberater-ctb":true,"xn--vermgensberatung-pwb":true,"xn--vhquv":true,"xn--vuq861b":true,"xn--xhq521b":true,"xn--zfr164b":true,"xyz":true,"yachts":true,"yahoo":true,"yamaxun":true,"yandex":true,"yodobashi":true,"yoga":true,"yokohama":true,"youtube":true,"yun":true,"zara":true,"zero":true,"zip":true,"zone":true,"zuerich":true,"cloudfront.net":true,"ap-northeast-1.compute.amazonaws.com":true,"ap-southeast-1.compute.amazonaws.com":true,"ap-southeast-2.compute.amazonaws.com":true,"cn-north-1.compute.amazonaws.cn":true,"compute.amazonaws.cn":true,"compute.amazonaws.com":true,"compute-1.amazonaws.com":true,"eu-west-1.compute.amazonaws.com":true,"eu-central-1.compute.amazonaws.com":true,"sa-east-1.compute.amazonaws.com":true,"us-east-1.amazonaws.com":true,"us-gov-west-1.compute.amazonaws.com":true,"us-west-1.compute.amazonaws.com":true,"us-west-2.compute.amazonaws.com":true,"z-1.compute-1.amazonaws.com":true,"z-2.compute-1.amazonaws.com":true,"elasticbeanstalk.com":true,"elb.amazonaws.com":true,"s3.amazonaws.com":true,"s3-us-west-2.amazonaws.com":true,"s3-us-west-1.amazonaws.com":true,"s3-eu-west-1.amazonaws.com":true,"s3-ap-southeast-1.amazonaws.com":true,"s3-ap-southeast-2.amazonaws.com":true,"s3-ap-northeast-1.amazonaws.com":true,"s3-sa-east-1.amazonaws.com":true,"s3-us-gov-west-1.amazonaws.com":true,"s3-fips-us-gov-west-1.amazonaws.com":true,"s3-website-us-east-1.amazonaws.com":true,"s3-website-us-west-2.amazonaws.com":true,"s3-website-us-west-1.amazonaws.com":true,"s3-website-eu-west-1.amazonaws.com":true,"s3-website-ap-southeast-1.amazonaws.com":true,"s3-website-ap-southeast-2.amazonaws.com":true,"s3-website-ap-northeast-1.amazonaws.com":true,"s3-website-sa-east-1.amazonaws.com":true,"s3-website-us-gov-west-1.amazonaws.com":true,"betainabox.com":true,"ae.org":true,"ar.com":true,"br.com":true,"cn.com":true,"com.de":true,"com.se":true,"de.com":true,"eu.com":true,"gb.com":true,"gb.net":true,"hu.com":true,"hu.net":true,"jp.net":true,"jpn.com":true,"kr.com":true,"mex.com":true,"no.com":true,"qc.com":true,"ru.com":true,"sa.com":true,"se.com":true,"se.net":true,"uk.com":true,"uk.net":true,"us.com":true,"uy.com":true,"za.bz":true,"za.com":true,"africa.com":true,"gr.com":true,"in.net":true,"us.org":true,"co.com":true,"c.la":true,"cloudcontrolled.com":true,"cloudcontrolapp.com":true,"co.ca":true,"co.nl":true,"co.no":true,"*.platform.sh":true,"cupcake.is":true,"dreamhosters.com":true,"dyndns-at-home.com":true,"dyndns-at-work.com":true,"dyndns-blog.com":true,"dyndns-free.com":true,"dyndns-home.com":true,"dyndns-ip.com":true,"dyndns-mail.com":true,"dyndns-office.com":true,"dyndns-pics.com":true,"dyndns-remote.com":true,"dyndns-server.com":true,"dyndns-web.com":true,"dyndns-wiki.com":true,"dyndns-work.com":true,"dyndns.biz":true,"dyndns.info":true,"dyndns.org":true,"dyndns.tv":true,"at-band-camp.net":true,"ath.cx":true,"barrel-of-knowledge.info":true,"barrell-of-knowledge.info":true,"better-than.tv":true,"blogdns.com":true,"blogdns.net":true,"blogdns.org":true,"blogsite.org":true,"boldlygoingnowhere.org":true,"broke-it.net":true,"buyshouses.net":true,"cechire.com":true,"dnsalias.com":true,"dnsalias.net":true,"dnsalias.org":true,"dnsdojo.com":true,"dnsdojo.net":true,"dnsdojo.org":true,"does-it.net":true,"doesntexist.com":true,"doesntexist.org":true,"dontexist.com":true,"dontexist.net":true,"dontexist.org":true,"doomdns.com":true,"doomdns.org":true,"dvrdns.org":true,"dyn-o-saur.com":true,"dynalias.com":true,"dynalias.net":true,"dynalias.org":true,"dynathome.net":true,"dyndns.ws":true,"endofinternet.net":true,"endofinternet.org":true,"endoftheinternet.org":true,"est-a-la-maison.com":true,"est-a-la-masion.com":true,"est-le-patron.com":true,"est-mon-blogueur.com":true,"for-better.biz":true,"for-more.biz":true,"for-our.info":true,"for-some.biz":true,"for-the.biz":true,"forgot.her.name":true,"forgot.his.name":true,"from-ak.com":true,"from-al.com":true,"from-ar.com":true,"from-az.net":true,"from-ca.com":true,"from-co.net":true,"from-ct.com":true,"from-dc.com":true,"from-de.com":true,"from-fl.com":true,"from-ga.com":true,"from-hi.com":true,"from-ia.com":true,"from-id.com":true,"from-il.com":true,"from-in.com":true,"from-ks.com":true,"from-ky.com":true,"from-la.net":true,"from-ma.com":true,"from-md.com":true,"from-me.org":true,"from-mi.com":true,"from-mn.com":true,"from-mo.com":true,"from-ms.com":true,"from-mt.com":true,"from-nc.com":true,"from-nd.com":true,"from-ne.com":true,"from-nh.com":true,"from-nj.com":true,"from-nm.com":true,"from-nv.com":true,"from-ny.net":true,"from-oh.com":true,"from-ok.com":true,"from-or.com":true,"from-pa.com":true,"from-pr.com":true,"from-ri.com":true,"from-sc.com":true,"from-sd.com":true,"from-tn.com":true,"from-tx.com":true,"from-ut.com":true,"from-va.com":true,"from-vt.com":true,"from-wa.com":true,"from-wi.com":true,"from-wv.com":true,"from-wy.com":true,"ftpaccess.cc":true,"fuettertdasnetz.de":true,"game-host.org":true,"game-server.cc":true,"getmyip.com":true,"gets-it.net":true,"go.dyndns.org":true,"gotdns.com":true,"gotdns.org":true,"groks-the.info":true,"groks-this.info":true,"ham-radio-op.net":true,"here-for-more.info":true,"hobby-site.com":true,"hobby-site.org":true,"home.dyndns.org":true,"homedns.org":true,"homeftp.net":true,"homeftp.org":true,"homeip.net":true,"homelinux.com":true,"homelinux.net":true,"homelinux.org":true,"homeunix.com":true,"homeunix.net":true,"homeunix.org":true,"iamallama.com":true,"in-the-band.net":true,"is-a-anarchist.com":true,"is-a-blogger.com":true,"is-a-bookkeeper.com":true,"is-a-bruinsfan.org":true,"is-a-bulls-fan.com":true,"is-a-candidate.org":true,"is-a-caterer.com":true,"is-a-celticsfan.org":true,"is-a-chef.com":true,"is-a-chef.net":true,"is-a-chef.org":true,"is-a-conservative.com":true,"is-a-cpa.com":true,"is-a-cubicle-slave.com":true,"is-a-democrat.com":true,"is-a-designer.com":true,"is-a-doctor.com":true,"is-a-financialadvisor.com":true,"is-a-geek.com":true,"is-a-geek.net":true,"is-a-geek.org":true,"is-a-green.com":true,"is-a-guru.com":true,"is-a-hard-worker.com":true,"is-a-hunter.com":true,"is-a-knight.org":true,"is-a-landscaper.com":true,"is-a-lawyer.com":true,"is-a-liberal.com":true,"is-a-libertarian.com":true,"is-a-linux-user.org":true,"is-a-llama.com":true,"is-a-musician.com":true,"is-a-nascarfan.com":true,"is-a-nurse.com":true,"is-a-painter.com":true,"is-a-patsfan.org":true,"is-a-personaltrainer.com":true,"is-a-photographer.com":true,"is-a-player.com":true,"is-a-republican.com":true,"is-a-rockstar.com":true,"is-a-socialist.com":true,"is-a-soxfan.org":true,"is-a-student.com":true,"is-a-teacher.com":true,"is-a-techie.com":true,"is-a-therapist.com":true,"is-an-accountant.com":true,"is-an-actor.com":true,"is-an-actress.com":true,"is-an-anarchist.com":true,"is-an-artist.com":true,"is-an-engineer.com":true,"is-an-entertainer.com":true,"is-by.us":true,"is-certified.com":true,"is-found.org":true,"is-gone.com":true,"is-into-anime.com":true,"is-into-cars.com":true,"is-into-cartoons.com":true,"is-into-games.com":true,"is-leet.com":true,"is-lost.org":true,"is-not-certified.com":true,"is-saved.org":true,"is-slick.com":true,"is-uberleet.com":true,"is-very-bad.org":true,"is-very-evil.org":true,"is-very-good.org":true,"is-very-nice.org":true,"is-very-sweet.org":true,"is-with-theband.com":true,"isa-geek.com":true,"isa-geek.net":true,"isa-geek.org":true,"isa-hockeynut.com":true,"issmarterthanyou.com":true,"isteingeek.de":true,"istmein.de":true,"kicks-ass.net":true,"kicks-ass.org":true,"knowsitall.info":true,"land-4-sale.us":true,"lebtimnetz.de":true,"leitungsen.de":true,"likes-pie.com":true,"likescandy.com":true,"merseine.nu":true,"mine.nu":true,"misconfused.org":true,"mypets.ws":true,"myphotos.cc":true,"neat-url.com":true,"office-on-the.net":true,"on-the-web.tv":true,"podzone.net":true,"podzone.org":true,"readmyblog.org":true,"saves-the-whales.com":true,"scrapper-site.net":true,"scrapping.cc":true,"selfip.biz":true,"selfip.com":true,"selfip.info":true,"selfip.net":true,"selfip.org":true,"sells-for-less.com":true,"sells-for-u.com":true,"sells-it.net":true,"sellsyourhome.org":true,"servebbs.com":true,"servebbs.net":true,"servebbs.org":true,"serveftp.net":true,"serveftp.org":true,"servegame.org":true,"shacknet.nu":true,"simple-url.com":true,"space-to-rent.com":true,"stuff-4-sale.org":true,"stuff-4-sale.us":true,"teaches-yoga.com":true,"thruhere.net":true,"traeumtgerade.de":true,"webhop.biz":true,"webhop.info":true,"webhop.net":true,"webhop.org":true,"worse-than.tv":true,"writesthisblog.com":true,"a.ssl.fastly.net":true,"b.ssl.fastly.net":true,"global.ssl.fastly.net":true,"a.prod.fastly.net":true,"global.prod.fastly.net":true,"firebaseapp.com":true,"flynnhub.com":true,"service.gov.uk":true,"github.io":true,"githubusercontent.com":true,"ro.com":true,"appspot.com":true,"blogspot.ae":true,"blogspot.be":true,"blogspot.bj":true,"blogspot.ca":true,"blogspot.cf":true,"blogspot.ch":true,"blogspot.co.at":true,"blogspot.co.il":true,"blogspot.co.nz":true,"blogspot.co.uk":true,"blogspot.com":true,"blogspot.com.ar":true,"blogspot.com.au":true,"blogspot.com.br":true,"blogspot.com.es":true,"blogspot.com.tr":true,"blogspot.cv":true,"blogspot.cz":true,"blogspot.de":true,"blogspot.dk":true,"blogspot.fi":true,"blogspot.fr":true,"blogspot.gr":true,"blogspot.hk":true,"blogspot.hu":true,"blogspot.ie":true,"blogspot.in":true,"blogspot.it":true,"blogspot.jp":true,"blogspot.kr":true,"blogspot.mr":true,"blogspot.mx":true,"blogspot.nl":true,"blogspot.no":true,"blogspot.pt":true,"blogspot.re":true,"blogspot.ro":true,"blogspot.ru":true,"blogspot.se":true,"blogspot.sg":true,"blogspot.sk":true,"blogspot.td":true,"blogspot.tw":true,"codespot.com":true,"googleapis.com":true,"googlecode.com":true,"pagespeedmobilizer.com":true,"withgoogle.com":true,"herokuapp.com":true,"herokussl.com":true,"iki.fi":true,"biz.at":true,"info.at":true,"co.pl":true,"azurewebsites.net":true,"azure-mobile.net":true,"cloudapp.net":true,"nfshost.com":true,"nyc.mn":true,"nid.io":true,"operaunite.com":true,"outsystemscloud.com":true,"art.pl":true,"gliwice.pl":true,"krakow.pl":true,"poznan.pl":true,"wroc.pl":true,"zakopane.pl":true,"priv.at":true,"rhcloud.com":true,"sinaapp.com":true,"vipsinaapp.com":true,"1kapp.com":true,"gda.pl":true,"gdansk.pl":true,"gdynia.pl":true,"med.pl":true,"sopot.pl":true,"hk.com":true,"hk.org":true,"ltd.hk":true,"inc.hk":true,"yolasite.com":true,"za.net":true,"za.org":true});
+
+// END of automatically generated file
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+/*jshint unused:false */
+
+function Store() {
+}
+exports.Store = Store;
+
+// Stores may be synchronous, but are still required to use a
+// Continuation-Passing Style API. The CookieJar itself will expose a "*Sync"
+// API that converts from synchronous-callbacks to imperative style.
+Store.prototype.synchronous = false;
+
+Store.prototype.findCookie = function(domain, path, key, cb) {
+ throw new Error('findCookie is not implemented');
+};
+
+Store.prototype.findCookies = function(domain, path, cb) {
+ throw new Error('findCookies is not implemented');
+};
+
+Store.prototype.putCookie = function(cookie, cb) {
+ throw new Error('putCookie is not implemented');
+};
+
+Store.prototype.updateCookie = function(oldCookie, newCookie, cb) {
+ // recommended default implementation:
+ // return this.putCookie(newCookie, cb);
+ throw new Error('updateCookie is not implemented');
+};
+
+Store.prototype.removeCookie = function(domain, path, key, cb) {
+ throw new Error('removeCookie is not implemented');
+};
+
+Store.prototype.removeCookies = function removeCookies(domain, path, cb) {
+ throw new Error('removeCookies is not implemented');
+};
--- /dev/null
+{
+ "author": {
+ "name": "Jeremy Stashewsky",
+ "email": "jstashewsky@salesforce.com"
+ },
+ "license": "BSD-3-Clause",
+ "name": "tough-cookie",
+ "description": "RFC6265 Cookies and Cookie Jar for node.js",
+ "keywords": [
+ "HTTP",
+ "cookie",
+ "cookies",
+ "set-cookie",
+ "cookiejar",
+ "jar",
+ "RFC6265",
+ "RFC2965"
+ ],
+ "version": "1.1.0",
+ "homepage": "https://github.com/SalesforceEng/tough-cookie",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/SalesforceEng/tough-cookie.git"
+ },
+ "bugs": {
+ "url": "https://github.com/SalesforceEng/tough-cookie/issues"
+ },
+ "main": "./lib/cookie",
+ "scripts": {
+ "test": "vows test/*_test.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "devDependencies": {
+ "vows": "0.7.0",
+ "async": ">=0.1.12"
+ },
+ "gitHead": "ddbcc02c8c24726c68e36a67d5864291acfdf57d",
+ "_id": "tough-cookie@1.1.0",
+ "_shasum": "126d2490e66ae5286b6863debd4a341076915954",
+ "_from": "tough-cookie@>=0.12.0",
+ "_npmVersion": "2.7.4",
+ "_nodeVersion": "0.12.2",
+ "_npmUser": {
+ "name": "jstash",
+ "email": "jstash@gmail.com"
+ },
+ "dist": {
+ "shasum": "126d2490e66ae5286b6863debd4a341076915954",
+ "tarball": "http://registry.npmjs.org/tough-cookie/-/tough-cookie-1.1.0.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "jstash",
+ "email": "jeremy@goinstant.com"
+ },
+ {
+ "name": "goinstant",
+ "email": "services@goinstant.com"
+ }
+ ],
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-1.1.0.tgz"
+}
--- /dev/null
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// ===BEGIN ICANN DOMAINS===
+
+// ac : http://en.wikipedia.org/wiki/.ac
+ac
+com.ac
+edu.ac
+gov.ac
+net.ac
+mil.ac
+org.ac
+
+// ad : http://en.wikipedia.org/wiki/.ad
+ad
+nom.ad
+
+// ae : http://en.wikipedia.org/wiki/.ae
+// see also: "Domain Name Eligibility Policy" at http://www.aeda.ae/eng/aepolicy.php
+ae
+co.ae
+net.ae
+org.ae
+sch.ae
+ac.ae
+gov.ae
+mil.ae
+
+// aero : see http://www.information.aero/index.php?id=66
+aero
+accident-investigation.aero
+accident-prevention.aero
+aerobatic.aero
+aeroclub.aero
+aerodrome.aero
+agents.aero
+aircraft.aero
+airline.aero
+airport.aero
+air-surveillance.aero
+airtraffic.aero
+air-traffic-control.aero
+ambulance.aero
+amusement.aero
+association.aero
+author.aero
+ballooning.aero
+broker.aero
+caa.aero
+cargo.aero
+catering.aero
+certification.aero
+championship.aero
+charter.aero
+civilaviation.aero
+club.aero
+conference.aero
+consultant.aero
+consulting.aero
+control.aero
+council.aero
+crew.aero
+design.aero
+dgca.aero
+educator.aero
+emergency.aero
+engine.aero
+engineer.aero
+entertainment.aero
+equipment.aero
+exchange.aero
+express.aero
+federation.aero
+flight.aero
+freight.aero
+fuel.aero
+gliding.aero
+government.aero
+groundhandling.aero
+group.aero
+hanggliding.aero
+homebuilt.aero
+insurance.aero
+journal.aero
+journalist.aero
+leasing.aero
+logistics.aero
+magazine.aero
+maintenance.aero
+marketplace.aero
+media.aero
+microlight.aero
+modelling.aero
+navigation.aero
+parachuting.aero
+paragliding.aero
+passenger-association.aero
+pilot.aero
+press.aero
+production.aero
+recreation.aero
+repbody.aero
+res.aero
+research.aero
+rotorcraft.aero
+safety.aero
+scientist.aero
+services.aero
+show.aero
+skydiving.aero
+software.aero
+student.aero
+taxi.aero
+trader.aero
+trading.aero
+trainer.aero
+union.aero
+workinggroup.aero
+works.aero
+
+// af : http://www.nic.af/help.jsp
+af
+gov.af
+com.af
+org.af
+net.af
+edu.af
+
+// ag : http://www.nic.ag/prices.htm
+ag
+com.ag
+org.ag
+net.ag
+co.ag
+nom.ag
+
+// ai : http://nic.com.ai/
+ai
+off.ai
+com.ai
+net.ai
+org.ai
+
+// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31
+al
+com.al
+edu.al
+gov.al
+mil.al
+net.al
+org.al
+
+// am : http://en.wikipedia.org/wiki/.am
+am
+
+// an : http://www.una.an/an_domreg/default.asp
+an
+com.an
+net.an
+org.an
+edu.an
+
+// ao : http://en.wikipedia.org/wiki/.ao
+// http://www.dns.ao/REGISTR.DOC
+ao
+ed.ao
+gv.ao
+og.ao
+co.ao
+pb.ao
+it.ao
+
+// aq : http://en.wikipedia.org/wiki/.aq
+aq
+
+// ar : https://nic.ar/normativa-vigente.xhtml
+ar
+com.ar
+edu.ar
+gob.ar
+gov.ar
+int.ar
+mil.ar
+net.ar
+org.ar
+tur.ar
+
+// arpa : http://en.wikipedia.org/wiki/.arpa
+// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+arpa
+e164.arpa
+in-addr.arpa
+ip6.arpa
+iris.arpa
+uri.arpa
+urn.arpa
+
+// as : http://en.wikipedia.org/wiki/.as
+as
+gov.as
+
+// asia : http://en.wikipedia.org/wiki/.asia
+asia
+
+// at : http://en.wikipedia.org/wiki/.at
+// Confirmed by registry <it@nic.at> 2008-06-17
+at
+ac.at
+co.at
+gv.at
+or.at
+
+// au : http://en.wikipedia.org/wiki/.au
+// http://www.auda.org.au/
+au
+// 2LDs
+com.au
+net.au
+org.au
+edu.au
+gov.au
+asn.au
+id.au
+// Historic 2LDs (closed to new registration, but sites still exist)
+info.au
+conf.au
+oz.au
+// CGDNs - http://www.cgdn.org.au/
+act.au
+nsw.au
+nt.au
+qld.au
+sa.au
+tas.au
+vic.au
+wa.au
+// 3LDs
+act.edu.au
+nsw.edu.au
+nt.edu.au
+qld.edu.au
+sa.edu.au
+tas.edu.au
+vic.edu.au
+wa.edu.au
+// act.gov.au Bug 984824 - Removed at request of Greg Tankard
+// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au>
+// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au>
+qld.gov.au
+sa.gov.au
+tas.gov.au
+vic.gov.au
+wa.gov.au
+
+// aw : http://en.wikipedia.org/wiki/.aw
+aw
+com.aw
+
+// ax : http://en.wikipedia.org/wiki/.ax
+ax
+
+// az : http://en.wikipedia.org/wiki/.az
+az
+com.az
+net.az
+int.az
+gov.az
+org.az
+edu.az
+info.az
+pp.az
+mil.az
+name.az
+pro.az
+biz.az
+
+// ba : http://en.wikipedia.org/wiki/.ba
+ba
+org.ba
+net.ba
+edu.ba
+gov.ba
+mil.ba
+unsa.ba
+unbi.ba
+co.ba
+com.ba
+rs.ba
+
+// bb : http://en.wikipedia.org/wiki/.bb
+bb
+biz.bb
+co.bb
+com.bb
+edu.bb
+gov.bb
+info.bb
+net.bb
+org.bb
+store.bb
+tv.bb
+
+// bd : http://en.wikipedia.org/wiki/.bd
+*.bd
+
+// be : http://en.wikipedia.org/wiki/.be
+// Confirmed by registry <tech@dns.be> 2008-06-08
+be
+ac.be
+
+// bf : http://en.wikipedia.org/wiki/.bf
+bf
+gov.bf
+
+// bg : http://en.wikipedia.org/wiki/.bg
+// https://www.register.bg/user/static/rules/en/index.html
+bg
+a.bg
+b.bg
+c.bg
+d.bg
+e.bg
+f.bg
+g.bg
+h.bg
+i.bg
+j.bg
+k.bg
+l.bg
+m.bg
+n.bg
+o.bg
+p.bg
+q.bg
+r.bg
+s.bg
+t.bg
+u.bg
+v.bg
+w.bg
+x.bg
+y.bg
+z.bg
+0.bg
+1.bg
+2.bg
+3.bg
+4.bg
+5.bg
+6.bg
+7.bg
+8.bg
+9.bg
+
+// bh : http://en.wikipedia.org/wiki/.bh
+bh
+com.bh
+edu.bh
+net.bh
+org.bh
+gov.bh
+
+// bi : http://en.wikipedia.org/wiki/.bi
+// http://whois.nic.bi/
+bi
+co.bi
+com.bi
+edu.bi
+or.bi
+org.bi
+
+// biz : http://en.wikipedia.org/wiki/.biz
+biz
+
+// bj : http://en.wikipedia.org/wiki/.bj
+bj
+asso.bj
+barreau.bj
+gouv.bj
+
+// bm : http://www.bermudanic.bm/dnr-text.txt
+bm
+com.bm
+edu.bm
+gov.bm
+net.bm
+org.bm
+
+// bn : http://en.wikipedia.org/wiki/.bn
+*.bn
+
+// bo : http://www.nic.bo/
+bo
+com.bo
+edu.bo
+gov.bo
+gob.bo
+int.bo
+org.bo
+net.bo
+mil.bo
+tv.bo
+
+// br : http://registro.br/dominio/categoria.html
+// Submitted by registry <fneves@registro.br> 2014-08-11
+br
+adm.br
+adv.br
+agr.br
+am.br
+arq.br
+art.br
+ato.br
+b.br
+bio.br
+blog.br
+bmd.br
+cim.br
+cng.br
+cnt.br
+com.br
+coop.br
+ecn.br
+eco.br
+edu.br
+emp.br
+eng.br
+esp.br
+etc.br
+eti.br
+far.br
+flog.br
+fm.br
+fnd.br
+fot.br
+fst.br
+g12.br
+ggf.br
+gov.br
+imb.br
+ind.br
+inf.br
+jor.br
+jus.br
+leg.br
+lel.br
+mat.br
+med.br
+mil.br
+mp.br
+mus.br
+net.br
+*.nom.br
+not.br
+ntr.br
+odo.br
+org.br
+ppg.br
+pro.br
+psc.br
+psi.br
+qsl.br
+radio.br
+rec.br
+slg.br
+srv.br
+taxi.br
+teo.br
+tmp.br
+trd.br
+tur.br
+tv.br
+vet.br
+vlog.br
+wiki.br
+zlg.br
+
+// bs : http://www.nic.bs/rules.html
+bs
+com.bs
+net.bs
+org.bs
+edu.bs
+gov.bs
+
+// bt : http://en.wikipedia.org/wiki/.bt
+bt
+com.bt
+edu.bt
+gov.bt
+net.bt
+org.bt
+
+// bv : No registrations at this time.
+// Submitted by registry <jarle@uninett.no> 2006-06-16
+bv
+
+// bw : http://en.wikipedia.org/wiki/.bw
+// http://www.gobin.info/domainname/bw.doc
+// list of other 2nd level tlds ?
+bw
+co.bw
+org.bw
+
+// by : http://en.wikipedia.org/wiki/.by
+// http://tld.by/rules_2006_en.html
+// list of other 2nd level tlds ?
+by
+gov.by
+mil.by
+// Official information does not indicate that com.by is a reserved
+// second-level domain, but it's being used as one (see www.google.com.by and
+// www.yahoo.com.by, for example), so we list it here for safety's sake.
+com.by
+
+// http://hoster.by/
+of.by
+
+// bz : http://en.wikipedia.org/wiki/.bz
+// http://www.belizenic.bz/
+bz
+com.bz
+net.bz
+org.bz
+edu.bz
+gov.bz
+
+// ca : http://en.wikipedia.org/wiki/.ca
+ca
+// ca geographical names
+ab.ca
+bc.ca
+mb.ca
+nb.ca
+nf.ca
+nl.ca
+ns.ca
+nt.ca
+nu.ca
+on.ca
+pe.ca
+qc.ca
+sk.ca
+yk.ca
+// gc.ca: http://en.wikipedia.org/wiki/.gc.ca
+// see also: http://registry.gc.ca/en/SubdomainFAQ
+gc.ca
+
+// cat : http://en.wikipedia.org/wiki/.cat
+cat
+
+// cc : http://en.wikipedia.org/wiki/.cc
+cc
+
+// cd : http://en.wikipedia.org/wiki/.cd
+// see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1
+cd
+gov.cd
+
+// cf : http://en.wikipedia.org/wiki/.cf
+cf
+
+// cg : http://en.wikipedia.org/wiki/.cg
+cg
+
+// ch : http://en.wikipedia.org/wiki/.ch
+ch
+
+// ci : http://en.wikipedia.org/wiki/.ci
+// http://www.nic.ci/index.php?page=charte
+ci
+org.ci
+or.ci
+com.ci
+co.ci
+edu.ci
+ed.ci
+ac.ci
+net.ci
+go.ci
+asso.ci
+aéroport.ci
+int.ci
+presse.ci
+md.ci
+gouv.ci
+
+// ck : http://en.wikipedia.org/wiki/.ck
+*.ck
+!www.ck
+
+// cl : http://en.wikipedia.org/wiki/.cl
+cl
+gov.cl
+gob.cl
+co.cl
+mil.cl
+
+// cm : http://en.wikipedia.org/wiki/.cm plus bug 981927
+cm
+co.cm
+com.cm
+gov.cm
+net.cm
+
+// cn : http://en.wikipedia.org/wiki/.cn
+// Submitted by registry <tanyaling@cnnic.cn> 2008-06-11
+cn
+ac.cn
+com.cn
+edu.cn
+gov.cn
+net.cn
+org.cn
+mil.cn
+公司.cn
+网络.cn
+網絡.cn
+// cn geographic names
+ah.cn
+bj.cn
+cq.cn
+fj.cn
+gd.cn
+gs.cn
+gz.cn
+gx.cn
+ha.cn
+hb.cn
+he.cn
+hi.cn
+hl.cn
+hn.cn
+jl.cn
+js.cn
+jx.cn
+ln.cn
+nm.cn
+nx.cn
+qh.cn
+sc.cn
+sd.cn
+sh.cn
+sn.cn
+sx.cn
+tj.cn
+xj.cn
+xz.cn
+yn.cn
+zj.cn
+hk.cn
+mo.cn
+tw.cn
+
+// co : http://en.wikipedia.org/wiki/.co
+// Submitted by registry <tecnico@uniandes.edu.co> 2008-06-11
+co
+arts.co
+com.co
+edu.co
+firm.co
+gov.co
+info.co
+int.co
+mil.co
+net.co
+nom.co
+org.co
+rec.co
+web.co
+
+// com : http://en.wikipedia.org/wiki/.com
+com
+
+// coop : http://en.wikipedia.org/wiki/.coop
+coop
+
+// cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do
+cr
+ac.cr
+co.cr
+ed.cr
+fi.cr
+go.cr
+or.cr
+sa.cr
+
+// cu : http://en.wikipedia.org/wiki/.cu
+cu
+com.cu
+edu.cu
+org.cu
+net.cu
+gov.cu
+inf.cu
+
+// cv : http://en.wikipedia.org/wiki/.cv
+cv
+
+// cw : http://www.una.cw/cw_registry/
+// Confirmed by registry <registry@una.net> 2013-03-26
+cw
+com.cw
+edu.cw
+net.cw
+org.cw
+
+// cx : http://en.wikipedia.org/wiki/.cx
+// list of other 2nd level tlds ?
+cx
+gov.cx
+
+// cy : http://en.wikipedia.org/wiki/.cy
+*.cy
+
+// cz : http://en.wikipedia.org/wiki/.cz
+cz
+
+// de : http://en.wikipedia.org/wiki/.de
+// Confirmed by registry <ops@denic.de> (with technical
+// reservations) 2008-07-01
+de
+
+// dj : http://en.wikipedia.org/wiki/.dj
+dj
+
+// dk : http://en.wikipedia.org/wiki/.dk
+// Confirmed by registry <robert@dk-hostmaster.dk> 2008-06-17
+dk
+
+// dm : http://en.wikipedia.org/wiki/.dm
+dm
+com.dm
+net.dm
+org.dm
+edu.dm
+gov.dm
+
+// do : http://en.wikipedia.org/wiki/.do
+do
+art.do
+com.do
+edu.do
+gob.do
+gov.do
+mil.do
+net.do
+org.do
+sld.do
+web.do
+
+// dz : http://en.wikipedia.org/wiki/.dz
+dz
+com.dz
+org.dz
+net.dz
+gov.dz
+edu.dz
+asso.dz
+pol.dz
+art.dz
+
+// ec : http://www.nic.ec/reg/paso1.asp
+// Submitted by registry <vabboud@nic.ec> 2008-07-04
+ec
+com.ec
+info.ec
+net.ec
+fin.ec
+k12.ec
+med.ec
+pro.ec
+org.ec
+edu.ec
+gov.ec
+gob.ec
+mil.ec
+
+// edu : http://en.wikipedia.org/wiki/.edu
+edu
+
+// ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B
+ee
+edu.ee
+gov.ee
+riik.ee
+lib.ee
+med.ee
+com.ee
+pri.ee
+aip.ee
+org.ee
+fie.ee
+
+// eg : http://en.wikipedia.org/wiki/.eg
+eg
+com.eg
+edu.eg
+eun.eg
+gov.eg
+mil.eg
+name.eg
+net.eg
+org.eg
+sci.eg
+
+// er : http://en.wikipedia.org/wiki/.er
+*.er
+
+// es : https://www.nic.es/site_ingles/ingles/dominios/index.html
+es
+com.es
+nom.es
+org.es
+gob.es
+edu.es
+
+// et : http://en.wikipedia.org/wiki/.et
+et
+com.et
+gov.et
+org.et
+edu.et
+biz.et
+name.et
+info.et
+
+// eu : http://en.wikipedia.org/wiki/.eu
+eu
+
+// fi : http://en.wikipedia.org/wiki/.fi
+fi
+// aland.fi : http://en.wikipedia.org/wiki/.ax
+// This domain is being phased out in favor of .ax. As there are still many
+// domains under aland.fi, we still keep it on the list until aland.fi is
+// completely removed.
+// TODO: Check for updates (expected to be phased out around Q1/2009)
+aland.fi
+
+// fj : http://en.wikipedia.org/wiki/.fj
+*.fj
+
+// fk : http://en.wikipedia.org/wiki/.fk
+*.fk
+
+// fm : http://en.wikipedia.org/wiki/.fm
+fm
+
+// fo : http://en.wikipedia.org/wiki/.fo
+fo
+
+// fr : http://www.afnic.fr/
+// domaines descriptifs : http://www.afnic.fr/obtenir/chartes/nommage-fr/annexe-descriptifs
+fr
+com.fr
+asso.fr
+nom.fr
+prd.fr
+presse.fr
+tm.fr
+// domaines sectoriels : http://www.afnic.fr/obtenir/chartes/nommage-fr/annexe-sectoriels
+aeroport.fr
+assedic.fr
+avocat.fr
+avoues.fr
+cci.fr
+chambagri.fr
+chirurgiens-dentistes.fr
+experts-comptables.fr
+geometre-expert.fr
+gouv.fr
+greta.fr
+huissier-justice.fr
+medecin.fr
+notaires.fr
+pharmacien.fr
+port.fr
+veterinaire.fr
+
+// ga : http://en.wikipedia.org/wiki/.ga
+ga
+
+// gb : This registry is effectively dormant
+// Submitted by registry <Damien.Shaw@ja.net> 2008-06-12
+gb
+
+// gd : http://en.wikipedia.org/wiki/.gd
+gd
+
+// ge : http://www.nic.net.ge/policy_en.pdf
+ge
+com.ge
+edu.ge
+gov.ge
+org.ge
+mil.ge
+net.ge
+pvt.ge
+
+// gf : http://en.wikipedia.org/wiki/.gf
+gf
+
+// gg : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
+gg
+co.gg
+net.gg
+org.gg
+
+// gh : http://en.wikipedia.org/wiki/.gh
+// see also: http://www.nic.gh/reg_now.php
+// Although domains directly at second level are not possible at the moment,
+// they have been possible for some time and may come back.
+gh
+com.gh
+edu.gh
+gov.gh
+org.gh
+mil.gh
+
+// gi : http://www.nic.gi/rules.html
+gi
+com.gi
+ltd.gi
+gov.gi
+mod.gi
+edu.gi
+org.gi
+
+// gl : http://en.wikipedia.org/wiki/.gl
+// http://nic.gl
+gl
+
+// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm
+gm
+
+// gn : http://psg.com/dns/gn/gn.txt
+// Submitted by registry <randy@psg.com> 2008-06-17
+gn
+ac.gn
+com.gn
+edu.gn
+gov.gn
+org.gn
+net.gn
+
+// gov : http://en.wikipedia.org/wiki/.gov
+gov
+
+// gp : http://www.nic.gp/index.php?lang=en
+gp
+com.gp
+net.gp
+mobi.gp
+edu.gp
+org.gp
+asso.gp
+
+// gq : http://en.wikipedia.org/wiki/.gq
+gq
+
+// gr : https://grweb.ics.forth.gr/english/1617-B-2005.html
+// Submitted by registry <segred@ics.forth.gr> 2008-06-09
+gr
+com.gr
+edu.gr
+net.gr
+org.gr
+gov.gr
+
+// gs : http://en.wikipedia.org/wiki/.gs
+gs
+
+// gt : http://www.gt/politicas_de_registro.html
+gt
+com.gt
+edu.gt
+gob.gt
+ind.gt
+mil.gt
+net.gt
+org.gt
+
+// gu : http://gadao.gov.gu/registration.txt
+*.gu
+
+// gw : http://en.wikipedia.org/wiki/.gw
+gw
+
+// gy : http://en.wikipedia.org/wiki/.gy
+// http://registry.gy/
+gy
+co.gy
+com.gy
+net.gy
+
+// hk : https://www.hkdnr.hk
+// Submitted by registry <hk.tech@hkirc.hk> 2008-06-11
+hk
+com.hk
+edu.hk
+gov.hk
+idv.hk
+net.hk
+org.hk
+公司.hk
+教育.hk
+敎育.hk
+政府.hk
+個人.hk
+个人.hk
+箇人.hk
+網络.hk
+网络.hk
+组織.hk
+網絡.hk
+网絡.hk
+组织.hk
+組織.hk
+組织.hk
+
+// hm : http://en.wikipedia.org/wiki/.hm
+hm
+
+// hn : http://www.nic.hn/politicas/ps02,,05.html
+hn
+com.hn
+edu.hn
+org.hn
+net.hn
+mil.hn
+gob.hn
+
+// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf
+hr
+iz.hr
+from.hr
+name.hr
+com.hr
+
+// ht : http://www.nic.ht/info/charte.cfm
+ht
+com.ht
+shop.ht
+firm.ht
+info.ht
+adult.ht
+net.ht
+pro.ht
+org.ht
+med.ht
+art.ht
+coop.ht
+pol.ht
+asso.ht
+edu.ht
+rel.ht
+gouv.ht
+perso.ht
+
+// hu : http://www.domain.hu/domain/English/sld.html
+// Confirmed by registry <pasztor@iszt.hu> 2008-06-12
+hu
+co.hu
+info.hu
+org.hu
+priv.hu
+sport.hu
+tm.hu
+2000.hu
+agrar.hu
+bolt.hu
+casino.hu
+city.hu
+erotica.hu
+erotika.hu
+film.hu
+forum.hu
+games.hu
+hotel.hu
+ingatlan.hu
+jogasz.hu
+konyvelo.hu
+lakas.hu
+media.hu
+news.hu
+reklam.hu
+sex.hu
+shop.hu
+suli.hu
+szex.hu
+tozsde.hu
+utazas.hu
+video.hu
+
+// id : https://register.pandi.or.id/
+id
+ac.id
+biz.id
+co.id
+desa.id
+go.id
+mil.id
+my.id
+net.id
+or.id
+sch.id
+web.id
+
+// ie : http://en.wikipedia.org/wiki/.ie
+ie
+gov.ie
+
+// il : http://en.wikipedia.org/wiki/.il
+*.il
+
+// im : https://www.nic.im/
+// Submitted by registry <info@nic.im> 2013-11-15
+im
+ac.im
+co.im
+com.im
+ltd.co.im
+net.im
+org.im
+plc.co.im
+tt.im
+tv.im
+
+// in : http://en.wikipedia.org/wiki/.in
+// see also: https://registry.in/Policies
+// Please note, that nic.in is not an offical eTLD, but used by most
+// government institutions.
+in
+co.in
+firm.in
+net.in
+org.in
+gen.in
+ind.in
+nic.in
+ac.in
+edu.in
+res.in
+gov.in
+mil.in
+
+// info : http://en.wikipedia.org/wiki/.info
+info
+
+// int : http://en.wikipedia.org/wiki/.int
+// Confirmed by registry <iana-questions@icann.org> 2008-06-18
+int
+eu.int
+
+// io : http://www.nic.io/rules.html
+// list of other 2nd level tlds ?
+io
+com.io
+
+// iq : http://www.cmc.iq/english/iq/iqregister1.htm
+iq
+gov.iq
+edu.iq
+mil.iq
+com.iq
+org.iq
+net.iq
+
+// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules
+// Also see http://www.nic.ir/Internationalized_Domain_Names
+// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16
+ir
+ac.ir
+co.ir
+gov.ir
+id.ir
+net.ir
+org.ir
+sch.ir
+// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH)
+ایران.ir
+// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH)
+ايران.ir
+
+// is : http://www.isnic.is/domain/rules.php
+// Confirmed by registry <marius@isgate.is> 2008-12-06
+is
+net.is
+com.is
+edu.is
+gov.is
+org.is
+int.is
+
+// it : http://en.wikipedia.org/wiki/.it
+it
+gov.it
+edu.it
+// Reserved geo-names:
+// http://www.nic.it/documenti/regolamenti-e-linee-guida/regolamento-assegnazione-versione-6.0.pdf
+// There is also a list of reserved geo-names corresponding to Italian municipalities
+// http://www.nic.it/documenti/appendice-c.pdf, but it is not included here.
+// Regions
+abr.it
+abruzzo.it
+aosta-valley.it
+aostavalley.it
+bas.it
+basilicata.it
+cal.it
+calabria.it
+cam.it
+campania.it
+emilia-romagna.it
+emiliaromagna.it
+emr.it
+friuli-v-giulia.it
+friuli-ve-giulia.it
+friuli-vegiulia.it
+friuli-venezia-giulia.it
+friuli-veneziagiulia.it
+friuli-vgiulia.it
+friuliv-giulia.it
+friulive-giulia.it
+friulivegiulia.it
+friulivenezia-giulia.it
+friuliveneziagiulia.it
+friulivgiulia.it
+fvg.it
+laz.it
+lazio.it
+lig.it
+liguria.it
+lom.it
+lombardia.it
+lombardy.it
+lucania.it
+mar.it
+marche.it
+mol.it
+molise.it
+piedmont.it
+piemonte.it
+pmn.it
+pug.it
+puglia.it
+sar.it
+sardegna.it
+sardinia.it
+sic.it
+sicilia.it
+sicily.it
+taa.it
+tos.it
+toscana.it
+trentino-a-adige.it
+trentino-aadige.it
+trentino-alto-adige.it
+trentino-altoadige.it
+trentino-s-tirol.it
+trentino-stirol.it
+trentino-sud-tirol.it
+trentino-sudtirol.it
+trentino-sued-tirol.it
+trentino-suedtirol.it
+trentinoa-adige.it
+trentinoaadige.it
+trentinoalto-adige.it
+trentinoaltoadige.it
+trentinos-tirol.it
+trentinostirol.it
+trentinosud-tirol.it
+trentinosudtirol.it
+trentinosued-tirol.it
+trentinosuedtirol.it
+tuscany.it
+umb.it
+umbria.it
+val-d-aosta.it
+val-daosta.it
+vald-aosta.it
+valdaosta.it
+valle-aosta.it
+valle-d-aosta.it
+valle-daosta.it
+valleaosta.it
+valled-aosta.it
+valledaosta.it
+vallee-aoste.it
+valleeaoste.it
+vao.it
+vda.it
+ven.it
+veneto.it
+// Provinces
+ag.it
+agrigento.it
+al.it
+alessandria.it
+alto-adige.it
+altoadige.it
+an.it
+ancona.it
+andria-barletta-trani.it
+andria-trani-barletta.it
+andriabarlettatrani.it
+andriatranibarletta.it
+ao.it
+aosta.it
+aoste.it
+ap.it
+aq.it
+aquila.it
+ar.it
+arezzo.it
+ascoli-piceno.it
+ascolipiceno.it
+asti.it
+at.it
+av.it
+avellino.it
+ba.it
+balsan.it
+bari.it
+barletta-trani-andria.it
+barlettatraniandria.it
+belluno.it
+benevento.it
+bergamo.it
+bg.it
+bi.it
+biella.it
+bl.it
+bn.it
+bo.it
+bologna.it
+bolzano.it
+bozen.it
+br.it
+brescia.it
+brindisi.it
+bs.it
+bt.it
+bz.it
+ca.it
+cagliari.it
+caltanissetta.it
+campidano-medio.it
+campidanomedio.it
+campobasso.it
+carbonia-iglesias.it
+carboniaiglesias.it
+carrara-massa.it
+carraramassa.it
+caserta.it
+catania.it
+catanzaro.it
+cb.it
+ce.it
+cesena-forli.it
+cesenaforli.it
+ch.it
+chieti.it
+ci.it
+cl.it
+cn.it
+co.it
+como.it
+cosenza.it
+cr.it
+cremona.it
+crotone.it
+cs.it
+ct.it
+cuneo.it
+cz.it
+dell-ogliastra.it
+dellogliastra.it
+en.it
+enna.it
+fc.it
+fe.it
+fermo.it
+ferrara.it
+fg.it
+fi.it
+firenze.it
+florence.it
+fm.it
+foggia.it
+forli-cesena.it
+forlicesena.it
+fr.it
+frosinone.it
+ge.it
+genoa.it
+genova.it
+go.it
+gorizia.it
+gr.it
+grosseto.it
+iglesias-carbonia.it
+iglesiascarbonia.it
+im.it
+imperia.it
+is.it
+isernia.it
+kr.it
+la-spezia.it
+laquila.it
+laspezia.it
+latina.it
+lc.it
+le.it
+lecce.it
+lecco.it
+li.it
+livorno.it
+lo.it
+lodi.it
+lt.it
+lu.it
+lucca.it
+macerata.it
+mantova.it
+massa-carrara.it
+massacarrara.it
+matera.it
+mb.it
+mc.it
+me.it
+medio-campidano.it
+mediocampidano.it
+messina.it
+mi.it
+milan.it
+milano.it
+mn.it
+mo.it
+modena.it
+monza-brianza.it
+monza-e-della-brianza.it
+monza.it
+monzabrianza.it
+monzaebrianza.it
+monzaedellabrianza.it
+ms.it
+mt.it
+na.it
+naples.it
+napoli.it
+no.it
+novara.it
+nu.it
+nuoro.it
+og.it
+ogliastra.it
+olbia-tempio.it
+olbiatempio.it
+or.it
+oristano.it
+ot.it
+pa.it
+padova.it
+padua.it
+palermo.it
+parma.it
+pavia.it
+pc.it
+pd.it
+pe.it
+perugia.it
+pesaro-urbino.it
+pesarourbino.it
+pescara.it
+pg.it
+pi.it
+piacenza.it
+pisa.it
+pistoia.it
+pn.it
+po.it
+pordenone.it
+potenza.it
+pr.it
+prato.it
+pt.it
+pu.it
+pv.it
+pz.it
+ra.it
+ragusa.it
+ravenna.it
+rc.it
+re.it
+reggio-calabria.it
+reggio-emilia.it
+reggiocalabria.it
+reggioemilia.it
+rg.it
+ri.it
+rieti.it
+rimini.it
+rm.it
+rn.it
+ro.it
+roma.it
+rome.it
+rovigo.it
+sa.it
+salerno.it
+sassari.it
+savona.it
+si.it
+siena.it
+siracusa.it
+so.it
+sondrio.it
+sp.it
+sr.it
+ss.it
+suedtirol.it
+sv.it
+ta.it
+taranto.it
+te.it
+tempio-olbia.it
+tempioolbia.it
+teramo.it
+terni.it
+tn.it
+to.it
+torino.it
+tp.it
+tr.it
+trani-andria-barletta.it
+trani-barletta-andria.it
+traniandriabarletta.it
+tranibarlettaandria.it
+trapani.it
+trentino.it
+trento.it
+treviso.it
+trieste.it
+ts.it
+turin.it
+tv.it
+ud.it
+udine.it
+urbino-pesaro.it
+urbinopesaro.it
+va.it
+varese.it
+vb.it
+vc.it
+ve.it
+venezia.it
+venice.it
+verbania.it
+vercelli.it
+verona.it
+vi.it
+vibo-valentia.it
+vibovalentia.it
+vicenza.it
+viterbo.it
+vr.it
+vs.it
+vt.it
+vv.it
+
+// je : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
+je
+co.je
+net.je
+org.je
+
+// jm : http://www.com.jm/register.html
+*.jm
+
+// jo : http://www.dns.jo/Registration_policy.aspx
+jo
+com.jo
+org.jo
+net.jo
+edu.jo
+sch.jo
+gov.jo
+mil.jo
+name.jo
+
+// jobs : http://en.wikipedia.org/wiki/.jobs
+jobs
+
+// jp : http://en.wikipedia.org/wiki/.jp
+// http://jprs.co.jp/en/jpdomain.html
+// Submitted by registry <info@jprs.jp> 2014-10-30
+jp
+// jp organizational type names
+ac.jp
+ad.jp
+co.jp
+ed.jp
+go.jp
+gr.jp
+lg.jp
+ne.jp
+or.jp
+// jp prefecture type names
+aichi.jp
+akita.jp
+aomori.jp
+chiba.jp
+ehime.jp
+fukui.jp
+fukuoka.jp
+fukushima.jp
+gifu.jp
+gunma.jp
+hiroshima.jp
+hokkaido.jp
+hyogo.jp
+ibaraki.jp
+ishikawa.jp
+iwate.jp
+kagawa.jp
+kagoshima.jp
+kanagawa.jp
+kochi.jp
+kumamoto.jp
+kyoto.jp
+mie.jp
+miyagi.jp
+miyazaki.jp
+nagano.jp
+nagasaki.jp
+nara.jp
+niigata.jp
+oita.jp
+okayama.jp
+okinawa.jp
+osaka.jp
+saga.jp
+saitama.jp
+shiga.jp
+shimane.jp
+shizuoka.jp
+tochigi.jp
+tokushima.jp
+tokyo.jp
+tottori.jp
+toyama.jp
+wakayama.jp
+yamagata.jp
+yamaguchi.jp
+yamanashi.jp
+栃木.jp
+愛知.jp
+愛媛.jp
+兵庫.jp
+熊本.jp
+茨城.jp
+北海道.jp
+千葉.jp
+和歌山.jp
+長崎.jp
+長野.jp
+新潟.jp
+青森.jp
+静岡.jp
+東京.jp
+石川.jp
+埼玉.jp
+三重.jp
+京都.jp
+佐賀.jp
+大分.jp
+大阪.jp
+奈良.jp
+宮城.jp
+宮崎.jp
+富山.jp
+山口.jp
+山形.jp
+山梨.jp
+岩手.jp
+岐阜.jp
+岡山.jp
+島根.jp
+広島.jp
+徳島.jp
+沖縄.jp
+滋賀.jp
+神奈川.jp
+福井.jp
+福岡.jp
+福島.jp
+秋田.jp
+群馬.jp
+香川.jp
+高知.jp
+鳥取.jp
+鹿児島.jp
+// jp geographic type names
+// http://jprs.jp/doc/rule/saisoku-1.html
+*.kawasaki.jp
+*.kitakyushu.jp
+*.kobe.jp
+*.nagoya.jp
+*.sapporo.jp
+*.sendai.jp
+*.yokohama.jp
+!city.kawasaki.jp
+!city.kitakyushu.jp
+!city.kobe.jp
+!city.nagoya.jp
+!city.sapporo.jp
+!city.sendai.jp
+!city.yokohama.jp
+// 4th level registration
+aisai.aichi.jp
+ama.aichi.jp
+anjo.aichi.jp
+asuke.aichi.jp
+chiryu.aichi.jp
+chita.aichi.jp
+fuso.aichi.jp
+gamagori.aichi.jp
+handa.aichi.jp
+hazu.aichi.jp
+hekinan.aichi.jp
+higashiura.aichi.jp
+ichinomiya.aichi.jp
+inazawa.aichi.jp
+inuyama.aichi.jp
+isshiki.aichi.jp
+iwakura.aichi.jp
+kanie.aichi.jp
+kariya.aichi.jp
+kasugai.aichi.jp
+kira.aichi.jp
+kiyosu.aichi.jp
+komaki.aichi.jp
+konan.aichi.jp
+kota.aichi.jp
+mihama.aichi.jp
+miyoshi.aichi.jp
+nishio.aichi.jp
+nisshin.aichi.jp
+obu.aichi.jp
+oguchi.aichi.jp
+oharu.aichi.jp
+okazaki.aichi.jp
+owariasahi.aichi.jp
+seto.aichi.jp
+shikatsu.aichi.jp
+shinshiro.aichi.jp
+shitara.aichi.jp
+tahara.aichi.jp
+takahama.aichi.jp
+tobishima.aichi.jp
+toei.aichi.jp
+togo.aichi.jp
+tokai.aichi.jp
+tokoname.aichi.jp
+toyoake.aichi.jp
+toyohashi.aichi.jp
+toyokawa.aichi.jp
+toyone.aichi.jp
+toyota.aichi.jp
+tsushima.aichi.jp
+yatomi.aichi.jp
+akita.akita.jp
+daisen.akita.jp
+fujisato.akita.jp
+gojome.akita.jp
+hachirogata.akita.jp
+happou.akita.jp
+higashinaruse.akita.jp
+honjo.akita.jp
+honjyo.akita.jp
+ikawa.akita.jp
+kamikoani.akita.jp
+kamioka.akita.jp
+katagami.akita.jp
+kazuno.akita.jp
+kitaakita.akita.jp
+kosaka.akita.jp
+kyowa.akita.jp
+misato.akita.jp
+mitane.akita.jp
+moriyoshi.akita.jp
+nikaho.akita.jp
+noshiro.akita.jp
+odate.akita.jp
+oga.akita.jp
+ogata.akita.jp
+semboku.akita.jp
+yokote.akita.jp
+yurihonjo.akita.jp
+aomori.aomori.jp
+gonohe.aomori.jp
+hachinohe.aomori.jp
+hashikami.aomori.jp
+hiranai.aomori.jp
+hirosaki.aomori.jp
+itayanagi.aomori.jp
+kuroishi.aomori.jp
+misawa.aomori.jp
+mutsu.aomori.jp
+nakadomari.aomori.jp
+noheji.aomori.jp
+oirase.aomori.jp
+owani.aomori.jp
+rokunohe.aomori.jp
+sannohe.aomori.jp
+shichinohe.aomori.jp
+shingo.aomori.jp
+takko.aomori.jp
+towada.aomori.jp
+tsugaru.aomori.jp
+tsuruta.aomori.jp
+abiko.chiba.jp
+asahi.chiba.jp
+chonan.chiba.jp
+chosei.chiba.jp
+choshi.chiba.jp
+chuo.chiba.jp
+funabashi.chiba.jp
+futtsu.chiba.jp
+hanamigawa.chiba.jp
+ichihara.chiba.jp
+ichikawa.chiba.jp
+ichinomiya.chiba.jp
+inzai.chiba.jp
+isumi.chiba.jp
+kamagaya.chiba.jp
+kamogawa.chiba.jp
+kashiwa.chiba.jp
+katori.chiba.jp
+katsuura.chiba.jp
+kimitsu.chiba.jp
+kisarazu.chiba.jp
+kozaki.chiba.jp
+kujukuri.chiba.jp
+kyonan.chiba.jp
+matsudo.chiba.jp
+midori.chiba.jp
+mihama.chiba.jp
+minamiboso.chiba.jp
+mobara.chiba.jp
+mutsuzawa.chiba.jp
+nagara.chiba.jp
+nagareyama.chiba.jp
+narashino.chiba.jp
+narita.chiba.jp
+noda.chiba.jp
+oamishirasato.chiba.jp
+omigawa.chiba.jp
+onjuku.chiba.jp
+otaki.chiba.jp
+sakae.chiba.jp
+sakura.chiba.jp
+shimofusa.chiba.jp
+shirako.chiba.jp
+shiroi.chiba.jp
+shisui.chiba.jp
+sodegaura.chiba.jp
+sosa.chiba.jp
+tako.chiba.jp
+tateyama.chiba.jp
+togane.chiba.jp
+tohnosho.chiba.jp
+tomisato.chiba.jp
+urayasu.chiba.jp
+yachimata.chiba.jp
+yachiyo.chiba.jp
+yokaichiba.chiba.jp
+yokoshibahikari.chiba.jp
+yotsukaido.chiba.jp
+ainan.ehime.jp
+honai.ehime.jp
+ikata.ehime.jp
+imabari.ehime.jp
+iyo.ehime.jp
+kamijima.ehime.jp
+kihoku.ehime.jp
+kumakogen.ehime.jp
+masaki.ehime.jp
+matsuno.ehime.jp
+matsuyama.ehime.jp
+namikata.ehime.jp
+niihama.ehime.jp
+ozu.ehime.jp
+saijo.ehime.jp
+seiyo.ehime.jp
+shikokuchuo.ehime.jp
+tobe.ehime.jp
+toon.ehime.jp
+uchiko.ehime.jp
+uwajima.ehime.jp
+yawatahama.ehime.jp
+echizen.fukui.jp
+eiheiji.fukui.jp
+fukui.fukui.jp
+ikeda.fukui.jp
+katsuyama.fukui.jp
+mihama.fukui.jp
+minamiechizen.fukui.jp
+obama.fukui.jp
+ohi.fukui.jp
+ono.fukui.jp
+sabae.fukui.jp
+sakai.fukui.jp
+takahama.fukui.jp
+tsuruga.fukui.jp
+wakasa.fukui.jp
+ashiya.fukuoka.jp
+buzen.fukuoka.jp
+chikugo.fukuoka.jp
+chikuho.fukuoka.jp
+chikujo.fukuoka.jp
+chikushino.fukuoka.jp
+chikuzen.fukuoka.jp
+chuo.fukuoka.jp
+dazaifu.fukuoka.jp
+fukuchi.fukuoka.jp
+hakata.fukuoka.jp
+higashi.fukuoka.jp
+hirokawa.fukuoka.jp
+hisayama.fukuoka.jp
+iizuka.fukuoka.jp
+inatsuki.fukuoka.jp
+kaho.fukuoka.jp
+kasuga.fukuoka.jp
+kasuya.fukuoka.jp
+kawara.fukuoka.jp
+keisen.fukuoka.jp
+koga.fukuoka.jp
+kurate.fukuoka.jp
+kurogi.fukuoka.jp
+kurume.fukuoka.jp
+minami.fukuoka.jp
+miyako.fukuoka.jp
+miyama.fukuoka.jp
+miyawaka.fukuoka.jp
+mizumaki.fukuoka.jp
+munakata.fukuoka.jp
+nakagawa.fukuoka.jp
+nakama.fukuoka.jp
+nishi.fukuoka.jp
+nogata.fukuoka.jp
+ogori.fukuoka.jp
+okagaki.fukuoka.jp
+okawa.fukuoka.jp
+oki.fukuoka.jp
+omuta.fukuoka.jp
+onga.fukuoka.jp
+onojo.fukuoka.jp
+oto.fukuoka.jp
+saigawa.fukuoka.jp
+sasaguri.fukuoka.jp
+shingu.fukuoka.jp
+shinyoshitomi.fukuoka.jp
+shonai.fukuoka.jp
+soeda.fukuoka.jp
+sue.fukuoka.jp
+tachiarai.fukuoka.jp
+tagawa.fukuoka.jp
+takata.fukuoka.jp
+toho.fukuoka.jp
+toyotsu.fukuoka.jp
+tsuiki.fukuoka.jp
+ukiha.fukuoka.jp
+umi.fukuoka.jp
+usui.fukuoka.jp
+yamada.fukuoka.jp
+yame.fukuoka.jp
+yanagawa.fukuoka.jp
+yukuhashi.fukuoka.jp
+aizubange.fukushima.jp
+aizumisato.fukushima.jp
+aizuwakamatsu.fukushima.jp
+asakawa.fukushima.jp
+bandai.fukushima.jp
+date.fukushima.jp
+fukushima.fukushima.jp
+furudono.fukushima.jp
+futaba.fukushima.jp
+hanawa.fukushima.jp
+higashi.fukushima.jp
+hirata.fukushima.jp
+hirono.fukushima.jp
+iitate.fukushima.jp
+inawashiro.fukushima.jp
+ishikawa.fukushima.jp
+iwaki.fukushima.jp
+izumizaki.fukushima.jp
+kagamiishi.fukushima.jp
+kaneyama.fukushima.jp
+kawamata.fukushima.jp
+kitakata.fukushima.jp
+kitashiobara.fukushima.jp
+koori.fukushima.jp
+koriyama.fukushima.jp
+kunimi.fukushima.jp
+miharu.fukushima.jp
+mishima.fukushima.jp
+namie.fukushima.jp
+nango.fukushima.jp
+nishiaizu.fukushima.jp
+nishigo.fukushima.jp
+okuma.fukushima.jp
+omotego.fukushima.jp
+ono.fukushima.jp
+otama.fukushima.jp
+samegawa.fukushima.jp
+shimogo.fukushima.jp
+shirakawa.fukushima.jp
+showa.fukushima.jp
+soma.fukushima.jp
+sukagawa.fukushima.jp
+taishin.fukushima.jp
+tamakawa.fukushima.jp
+tanagura.fukushima.jp
+tenei.fukushima.jp
+yabuki.fukushima.jp
+yamato.fukushima.jp
+yamatsuri.fukushima.jp
+yanaizu.fukushima.jp
+yugawa.fukushima.jp
+anpachi.gifu.jp
+ena.gifu.jp
+gifu.gifu.jp
+ginan.gifu.jp
+godo.gifu.jp
+gujo.gifu.jp
+hashima.gifu.jp
+hichiso.gifu.jp
+hida.gifu.jp
+higashishirakawa.gifu.jp
+ibigawa.gifu.jp
+ikeda.gifu.jp
+kakamigahara.gifu.jp
+kani.gifu.jp
+kasahara.gifu.jp
+kasamatsu.gifu.jp
+kawaue.gifu.jp
+kitagata.gifu.jp
+mino.gifu.jp
+minokamo.gifu.jp
+mitake.gifu.jp
+mizunami.gifu.jp
+motosu.gifu.jp
+nakatsugawa.gifu.jp
+ogaki.gifu.jp
+sakahogi.gifu.jp
+seki.gifu.jp
+sekigahara.gifu.jp
+shirakawa.gifu.jp
+tajimi.gifu.jp
+takayama.gifu.jp
+tarui.gifu.jp
+toki.gifu.jp
+tomika.gifu.jp
+wanouchi.gifu.jp
+yamagata.gifu.jp
+yaotsu.gifu.jp
+yoro.gifu.jp
+annaka.gunma.jp
+chiyoda.gunma.jp
+fujioka.gunma.jp
+higashiagatsuma.gunma.jp
+isesaki.gunma.jp
+itakura.gunma.jp
+kanna.gunma.jp
+kanra.gunma.jp
+katashina.gunma.jp
+kawaba.gunma.jp
+kiryu.gunma.jp
+kusatsu.gunma.jp
+maebashi.gunma.jp
+meiwa.gunma.jp
+midori.gunma.jp
+minakami.gunma.jp
+naganohara.gunma.jp
+nakanojo.gunma.jp
+nanmoku.gunma.jp
+numata.gunma.jp
+oizumi.gunma.jp
+ora.gunma.jp
+ota.gunma.jp
+shibukawa.gunma.jp
+shimonita.gunma.jp
+shinto.gunma.jp
+showa.gunma.jp
+takasaki.gunma.jp
+takayama.gunma.jp
+tamamura.gunma.jp
+tatebayashi.gunma.jp
+tomioka.gunma.jp
+tsukiyono.gunma.jp
+tsumagoi.gunma.jp
+ueno.gunma.jp
+yoshioka.gunma.jp
+asaminami.hiroshima.jp
+daiwa.hiroshima.jp
+etajima.hiroshima.jp
+fuchu.hiroshima.jp
+fukuyama.hiroshima.jp
+hatsukaichi.hiroshima.jp
+higashihiroshima.hiroshima.jp
+hongo.hiroshima.jp
+jinsekikogen.hiroshima.jp
+kaita.hiroshima.jp
+kui.hiroshima.jp
+kumano.hiroshima.jp
+kure.hiroshima.jp
+mihara.hiroshima.jp
+miyoshi.hiroshima.jp
+naka.hiroshima.jp
+onomichi.hiroshima.jp
+osakikamijima.hiroshima.jp
+otake.hiroshima.jp
+saka.hiroshima.jp
+sera.hiroshima.jp
+seranishi.hiroshima.jp
+shinichi.hiroshima.jp
+shobara.hiroshima.jp
+takehara.hiroshima.jp
+abashiri.hokkaido.jp
+abira.hokkaido.jp
+aibetsu.hokkaido.jp
+akabira.hokkaido.jp
+akkeshi.hokkaido.jp
+asahikawa.hokkaido.jp
+ashibetsu.hokkaido.jp
+ashoro.hokkaido.jp
+assabu.hokkaido.jp
+atsuma.hokkaido.jp
+bibai.hokkaido.jp
+biei.hokkaido.jp
+bifuka.hokkaido.jp
+bihoro.hokkaido.jp
+biratori.hokkaido.jp
+chippubetsu.hokkaido.jp
+chitose.hokkaido.jp
+date.hokkaido.jp
+ebetsu.hokkaido.jp
+embetsu.hokkaido.jp
+eniwa.hokkaido.jp
+erimo.hokkaido.jp
+esan.hokkaido.jp
+esashi.hokkaido.jp
+fukagawa.hokkaido.jp
+fukushima.hokkaido.jp
+furano.hokkaido.jp
+furubira.hokkaido.jp
+haboro.hokkaido.jp
+hakodate.hokkaido.jp
+hamatonbetsu.hokkaido.jp
+hidaka.hokkaido.jp
+higashikagura.hokkaido.jp
+higashikawa.hokkaido.jp
+hiroo.hokkaido.jp
+hokuryu.hokkaido.jp
+hokuto.hokkaido.jp
+honbetsu.hokkaido.jp
+horokanai.hokkaido.jp
+horonobe.hokkaido.jp
+ikeda.hokkaido.jp
+imakane.hokkaido.jp
+ishikari.hokkaido.jp
+iwamizawa.hokkaido.jp
+iwanai.hokkaido.jp
+kamifurano.hokkaido.jp
+kamikawa.hokkaido.jp
+kamishihoro.hokkaido.jp
+kamisunagawa.hokkaido.jp
+kamoenai.hokkaido.jp
+kayabe.hokkaido.jp
+kembuchi.hokkaido.jp
+kikonai.hokkaido.jp
+kimobetsu.hokkaido.jp
+kitahiroshima.hokkaido.jp
+kitami.hokkaido.jp
+kiyosato.hokkaido.jp
+koshimizu.hokkaido.jp
+kunneppu.hokkaido.jp
+kuriyama.hokkaido.jp
+kuromatsunai.hokkaido.jp
+kushiro.hokkaido.jp
+kutchan.hokkaido.jp
+kyowa.hokkaido.jp
+mashike.hokkaido.jp
+matsumae.hokkaido.jp
+mikasa.hokkaido.jp
+minamifurano.hokkaido.jp
+mombetsu.hokkaido.jp
+moseushi.hokkaido.jp
+mukawa.hokkaido.jp
+muroran.hokkaido.jp
+naie.hokkaido.jp
+nakagawa.hokkaido.jp
+nakasatsunai.hokkaido.jp
+nakatombetsu.hokkaido.jp
+nanae.hokkaido.jp
+nanporo.hokkaido.jp
+nayoro.hokkaido.jp
+nemuro.hokkaido.jp
+niikappu.hokkaido.jp
+niki.hokkaido.jp
+nishiokoppe.hokkaido.jp
+noboribetsu.hokkaido.jp
+numata.hokkaido.jp
+obihiro.hokkaido.jp
+obira.hokkaido.jp
+oketo.hokkaido.jp
+okoppe.hokkaido.jp
+otaru.hokkaido.jp
+otobe.hokkaido.jp
+otofuke.hokkaido.jp
+otoineppu.hokkaido.jp
+oumu.hokkaido.jp
+ozora.hokkaido.jp
+pippu.hokkaido.jp
+rankoshi.hokkaido.jp
+rebun.hokkaido.jp
+rikubetsu.hokkaido.jp
+rishiri.hokkaido.jp
+rishirifuji.hokkaido.jp
+saroma.hokkaido.jp
+sarufutsu.hokkaido.jp
+shakotan.hokkaido.jp
+shari.hokkaido.jp
+shibecha.hokkaido.jp
+shibetsu.hokkaido.jp
+shikabe.hokkaido.jp
+shikaoi.hokkaido.jp
+shimamaki.hokkaido.jp
+shimizu.hokkaido.jp
+shimokawa.hokkaido.jp
+shinshinotsu.hokkaido.jp
+shintoku.hokkaido.jp
+shiranuka.hokkaido.jp
+shiraoi.hokkaido.jp
+shiriuchi.hokkaido.jp
+sobetsu.hokkaido.jp
+sunagawa.hokkaido.jp
+taiki.hokkaido.jp
+takasu.hokkaido.jp
+takikawa.hokkaido.jp
+takinoue.hokkaido.jp
+teshikaga.hokkaido.jp
+tobetsu.hokkaido.jp
+tohma.hokkaido.jp
+tomakomai.hokkaido.jp
+tomari.hokkaido.jp
+toya.hokkaido.jp
+toyako.hokkaido.jp
+toyotomi.hokkaido.jp
+toyoura.hokkaido.jp
+tsubetsu.hokkaido.jp
+tsukigata.hokkaido.jp
+urakawa.hokkaido.jp
+urausu.hokkaido.jp
+uryu.hokkaido.jp
+utashinai.hokkaido.jp
+wakkanai.hokkaido.jp
+wassamu.hokkaido.jp
+yakumo.hokkaido.jp
+yoichi.hokkaido.jp
+aioi.hyogo.jp
+akashi.hyogo.jp
+ako.hyogo.jp
+amagasaki.hyogo.jp
+aogaki.hyogo.jp
+asago.hyogo.jp
+ashiya.hyogo.jp
+awaji.hyogo.jp
+fukusaki.hyogo.jp
+goshiki.hyogo.jp
+harima.hyogo.jp
+himeji.hyogo.jp
+ichikawa.hyogo.jp
+inagawa.hyogo.jp
+itami.hyogo.jp
+kakogawa.hyogo.jp
+kamigori.hyogo.jp
+kamikawa.hyogo.jp
+kasai.hyogo.jp
+kasuga.hyogo.jp
+kawanishi.hyogo.jp
+miki.hyogo.jp
+minamiawaji.hyogo.jp
+nishinomiya.hyogo.jp
+nishiwaki.hyogo.jp
+ono.hyogo.jp
+sanda.hyogo.jp
+sannan.hyogo.jp
+sasayama.hyogo.jp
+sayo.hyogo.jp
+shingu.hyogo.jp
+shinonsen.hyogo.jp
+shiso.hyogo.jp
+sumoto.hyogo.jp
+taishi.hyogo.jp
+taka.hyogo.jp
+takarazuka.hyogo.jp
+takasago.hyogo.jp
+takino.hyogo.jp
+tamba.hyogo.jp
+tatsuno.hyogo.jp
+toyooka.hyogo.jp
+yabu.hyogo.jp
+yashiro.hyogo.jp
+yoka.hyogo.jp
+yokawa.hyogo.jp
+ami.ibaraki.jp
+asahi.ibaraki.jp
+bando.ibaraki.jp
+chikusei.ibaraki.jp
+daigo.ibaraki.jp
+fujishiro.ibaraki.jp
+hitachi.ibaraki.jp
+hitachinaka.ibaraki.jp
+hitachiomiya.ibaraki.jp
+hitachiota.ibaraki.jp
+ibaraki.ibaraki.jp
+ina.ibaraki.jp
+inashiki.ibaraki.jp
+itako.ibaraki.jp
+iwama.ibaraki.jp
+joso.ibaraki.jp
+kamisu.ibaraki.jp
+kasama.ibaraki.jp
+kashima.ibaraki.jp
+kasumigaura.ibaraki.jp
+koga.ibaraki.jp
+miho.ibaraki.jp
+mito.ibaraki.jp
+moriya.ibaraki.jp
+naka.ibaraki.jp
+namegata.ibaraki.jp
+oarai.ibaraki.jp
+ogawa.ibaraki.jp
+omitama.ibaraki.jp
+ryugasaki.ibaraki.jp
+sakai.ibaraki.jp
+sakuragawa.ibaraki.jp
+shimodate.ibaraki.jp
+shimotsuma.ibaraki.jp
+shirosato.ibaraki.jp
+sowa.ibaraki.jp
+suifu.ibaraki.jp
+takahagi.ibaraki.jp
+tamatsukuri.ibaraki.jp
+tokai.ibaraki.jp
+tomobe.ibaraki.jp
+tone.ibaraki.jp
+toride.ibaraki.jp
+tsuchiura.ibaraki.jp
+tsukuba.ibaraki.jp
+uchihara.ibaraki.jp
+ushiku.ibaraki.jp
+yachiyo.ibaraki.jp
+yamagata.ibaraki.jp
+yawara.ibaraki.jp
+yuki.ibaraki.jp
+anamizu.ishikawa.jp
+hakui.ishikawa.jp
+hakusan.ishikawa.jp
+kaga.ishikawa.jp
+kahoku.ishikawa.jp
+kanazawa.ishikawa.jp
+kawakita.ishikawa.jp
+komatsu.ishikawa.jp
+nakanoto.ishikawa.jp
+nanao.ishikawa.jp
+nomi.ishikawa.jp
+nonoichi.ishikawa.jp
+noto.ishikawa.jp
+shika.ishikawa.jp
+suzu.ishikawa.jp
+tsubata.ishikawa.jp
+tsurugi.ishikawa.jp
+uchinada.ishikawa.jp
+wajima.ishikawa.jp
+fudai.iwate.jp
+fujisawa.iwate.jp
+hanamaki.iwate.jp
+hiraizumi.iwate.jp
+hirono.iwate.jp
+ichinohe.iwate.jp
+ichinoseki.iwate.jp
+iwaizumi.iwate.jp
+iwate.iwate.jp
+joboji.iwate.jp
+kamaishi.iwate.jp
+kanegasaki.iwate.jp
+karumai.iwate.jp
+kawai.iwate.jp
+kitakami.iwate.jp
+kuji.iwate.jp
+kunohe.iwate.jp
+kuzumaki.iwate.jp
+miyako.iwate.jp
+mizusawa.iwate.jp
+morioka.iwate.jp
+ninohe.iwate.jp
+noda.iwate.jp
+ofunato.iwate.jp
+oshu.iwate.jp
+otsuchi.iwate.jp
+rikuzentakata.iwate.jp
+shiwa.iwate.jp
+shizukuishi.iwate.jp
+sumita.iwate.jp
+tanohata.iwate.jp
+tono.iwate.jp
+yahaba.iwate.jp
+yamada.iwate.jp
+ayagawa.kagawa.jp
+higashikagawa.kagawa.jp
+kanonji.kagawa.jp
+kotohira.kagawa.jp
+manno.kagawa.jp
+marugame.kagawa.jp
+mitoyo.kagawa.jp
+naoshima.kagawa.jp
+sanuki.kagawa.jp
+tadotsu.kagawa.jp
+takamatsu.kagawa.jp
+tonosho.kagawa.jp
+uchinomi.kagawa.jp
+utazu.kagawa.jp
+zentsuji.kagawa.jp
+akune.kagoshima.jp
+amami.kagoshima.jp
+hioki.kagoshima.jp
+isa.kagoshima.jp
+isen.kagoshima.jp
+izumi.kagoshima.jp
+kagoshima.kagoshima.jp
+kanoya.kagoshima.jp
+kawanabe.kagoshima.jp
+kinko.kagoshima.jp
+kouyama.kagoshima.jp
+makurazaki.kagoshima.jp
+matsumoto.kagoshima.jp
+minamitane.kagoshima.jp
+nakatane.kagoshima.jp
+nishinoomote.kagoshima.jp
+satsumasendai.kagoshima.jp
+soo.kagoshima.jp
+tarumizu.kagoshima.jp
+yusui.kagoshima.jp
+aikawa.kanagawa.jp
+atsugi.kanagawa.jp
+ayase.kanagawa.jp
+chigasaki.kanagawa.jp
+ebina.kanagawa.jp
+fujisawa.kanagawa.jp
+hadano.kanagawa.jp
+hakone.kanagawa.jp
+hiratsuka.kanagawa.jp
+isehara.kanagawa.jp
+kaisei.kanagawa.jp
+kamakura.kanagawa.jp
+kiyokawa.kanagawa.jp
+matsuda.kanagawa.jp
+minamiashigara.kanagawa.jp
+miura.kanagawa.jp
+nakai.kanagawa.jp
+ninomiya.kanagawa.jp
+odawara.kanagawa.jp
+oi.kanagawa.jp
+oiso.kanagawa.jp
+sagamihara.kanagawa.jp
+samukawa.kanagawa.jp
+tsukui.kanagawa.jp
+yamakita.kanagawa.jp
+yamato.kanagawa.jp
+yokosuka.kanagawa.jp
+yugawara.kanagawa.jp
+zama.kanagawa.jp
+zushi.kanagawa.jp
+aki.kochi.jp
+geisei.kochi.jp
+hidaka.kochi.jp
+higashitsuno.kochi.jp
+ino.kochi.jp
+kagami.kochi.jp
+kami.kochi.jp
+kitagawa.kochi.jp
+kochi.kochi.jp
+mihara.kochi.jp
+motoyama.kochi.jp
+muroto.kochi.jp
+nahari.kochi.jp
+nakamura.kochi.jp
+nankoku.kochi.jp
+nishitosa.kochi.jp
+niyodogawa.kochi.jp
+ochi.kochi.jp
+okawa.kochi.jp
+otoyo.kochi.jp
+otsuki.kochi.jp
+sakawa.kochi.jp
+sukumo.kochi.jp
+susaki.kochi.jp
+tosa.kochi.jp
+tosashimizu.kochi.jp
+toyo.kochi.jp
+tsuno.kochi.jp
+umaji.kochi.jp
+yasuda.kochi.jp
+yusuhara.kochi.jp
+amakusa.kumamoto.jp
+arao.kumamoto.jp
+aso.kumamoto.jp
+choyo.kumamoto.jp
+gyokuto.kumamoto.jp
+hitoyoshi.kumamoto.jp
+kamiamakusa.kumamoto.jp
+kashima.kumamoto.jp
+kikuchi.kumamoto.jp
+kosa.kumamoto.jp
+kumamoto.kumamoto.jp
+mashiki.kumamoto.jp
+mifune.kumamoto.jp
+minamata.kumamoto.jp
+minamioguni.kumamoto.jp
+nagasu.kumamoto.jp
+nishihara.kumamoto.jp
+oguni.kumamoto.jp
+ozu.kumamoto.jp
+sumoto.kumamoto.jp
+takamori.kumamoto.jp
+uki.kumamoto.jp
+uto.kumamoto.jp
+yamaga.kumamoto.jp
+yamato.kumamoto.jp
+yatsushiro.kumamoto.jp
+ayabe.kyoto.jp
+fukuchiyama.kyoto.jp
+higashiyama.kyoto.jp
+ide.kyoto.jp
+ine.kyoto.jp
+joyo.kyoto.jp
+kameoka.kyoto.jp
+kamo.kyoto.jp
+kita.kyoto.jp
+kizu.kyoto.jp
+kumiyama.kyoto.jp
+kyotamba.kyoto.jp
+kyotanabe.kyoto.jp
+kyotango.kyoto.jp
+maizuru.kyoto.jp
+minami.kyoto.jp
+minamiyamashiro.kyoto.jp
+miyazu.kyoto.jp
+muko.kyoto.jp
+nagaokakyo.kyoto.jp
+nakagyo.kyoto.jp
+nantan.kyoto.jp
+oyamazaki.kyoto.jp
+sakyo.kyoto.jp
+seika.kyoto.jp
+tanabe.kyoto.jp
+uji.kyoto.jp
+ujitawara.kyoto.jp
+wazuka.kyoto.jp
+yamashina.kyoto.jp
+yawata.kyoto.jp
+asahi.mie.jp
+inabe.mie.jp
+ise.mie.jp
+kameyama.mie.jp
+kawagoe.mie.jp
+kiho.mie.jp
+kisosaki.mie.jp
+kiwa.mie.jp
+komono.mie.jp
+kumano.mie.jp
+kuwana.mie.jp
+matsusaka.mie.jp
+meiwa.mie.jp
+mihama.mie.jp
+minamiise.mie.jp
+misugi.mie.jp
+miyama.mie.jp
+nabari.mie.jp
+shima.mie.jp
+suzuka.mie.jp
+tado.mie.jp
+taiki.mie.jp
+taki.mie.jp
+tamaki.mie.jp
+toba.mie.jp
+tsu.mie.jp
+udono.mie.jp
+ureshino.mie.jp
+watarai.mie.jp
+yokkaichi.mie.jp
+furukawa.miyagi.jp
+higashimatsushima.miyagi.jp
+ishinomaki.miyagi.jp
+iwanuma.miyagi.jp
+kakuda.miyagi.jp
+kami.miyagi.jp
+kawasaki.miyagi.jp
+kesennuma.miyagi.jp
+marumori.miyagi.jp
+matsushima.miyagi.jp
+minamisanriku.miyagi.jp
+misato.miyagi.jp
+murata.miyagi.jp
+natori.miyagi.jp
+ogawara.miyagi.jp
+ohira.miyagi.jp
+onagawa.miyagi.jp
+osaki.miyagi.jp
+rifu.miyagi.jp
+semine.miyagi.jp
+shibata.miyagi.jp
+shichikashuku.miyagi.jp
+shikama.miyagi.jp
+shiogama.miyagi.jp
+shiroishi.miyagi.jp
+tagajo.miyagi.jp
+taiwa.miyagi.jp
+tome.miyagi.jp
+tomiya.miyagi.jp
+wakuya.miyagi.jp
+watari.miyagi.jp
+yamamoto.miyagi.jp
+zao.miyagi.jp
+aya.miyazaki.jp
+ebino.miyazaki.jp
+gokase.miyazaki.jp
+hyuga.miyazaki.jp
+kadogawa.miyazaki.jp
+kawaminami.miyazaki.jp
+kijo.miyazaki.jp
+kitagawa.miyazaki.jp
+kitakata.miyazaki.jp
+kitaura.miyazaki.jp
+kobayashi.miyazaki.jp
+kunitomi.miyazaki.jp
+kushima.miyazaki.jp
+mimata.miyazaki.jp
+miyakonojo.miyazaki.jp
+miyazaki.miyazaki.jp
+morotsuka.miyazaki.jp
+nichinan.miyazaki.jp
+nishimera.miyazaki.jp
+nobeoka.miyazaki.jp
+saito.miyazaki.jp
+shiiba.miyazaki.jp
+shintomi.miyazaki.jp
+takaharu.miyazaki.jp
+takanabe.miyazaki.jp
+takazaki.miyazaki.jp
+tsuno.miyazaki.jp
+achi.nagano.jp
+agematsu.nagano.jp
+anan.nagano.jp
+aoki.nagano.jp
+asahi.nagano.jp
+azumino.nagano.jp
+chikuhoku.nagano.jp
+chikuma.nagano.jp
+chino.nagano.jp
+fujimi.nagano.jp
+hakuba.nagano.jp
+hara.nagano.jp
+hiraya.nagano.jp
+iida.nagano.jp
+iijima.nagano.jp
+iiyama.nagano.jp
+iizuna.nagano.jp
+ikeda.nagano.jp
+ikusaka.nagano.jp
+ina.nagano.jp
+karuizawa.nagano.jp
+kawakami.nagano.jp
+kiso.nagano.jp
+kisofukushima.nagano.jp
+kitaaiki.nagano.jp
+komagane.nagano.jp
+komoro.nagano.jp
+matsukawa.nagano.jp
+matsumoto.nagano.jp
+miasa.nagano.jp
+minamiaiki.nagano.jp
+minamimaki.nagano.jp
+minamiminowa.nagano.jp
+minowa.nagano.jp
+miyada.nagano.jp
+miyota.nagano.jp
+mochizuki.nagano.jp
+nagano.nagano.jp
+nagawa.nagano.jp
+nagiso.nagano.jp
+nakagawa.nagano.jp
+nakano.nagano.jp
+nozawaonsen.nagano.jp
+obuse.nagano.jp
+ogawa.nagano.jp
+okaya.nagano.jp
+omachi.nagano.jp
+omi.nagano.jp
+ookuwa.nagano.jp
+ooshika.nagano.jp
+otaki.nagano.jp
+otari.nagano.jp
+sakae.nagano.jp
+sakaki.nagano.jp
+saku.nagano.jp
+sakuho.nagano.jp
+shimosuwa.nagano.jp
+shinanomachi.nagano.jp
+shiojiri.nagano.jp
+suwa.nagano.jp
+suzaka.nagano.jp
+takagi.nagano.jp
+takamori.nagano.jp
+takayama.nagano.jp
+tateshina.nagano.jp
+tatsuno.nagano.jp
+togakushi.nagano.jp
+togura.nagano.jp
+tomi.nagano.jp
+ueda.nagano.jp
+wada.nagano.jp
+yamagata.nagano.jp
+yamanouchi.nagano.jp
+yasaka.nagano.jp
+yasuoka.nagano.jp
+chijiwa.nagasaki.jp
+futsu.nagasaki.jp
+goto.nagasaki.jp
+hasami.nagasaki.jp
+hirado.nagasaki.jp
+iki.nagasaki.jp
+isahaya.nagasaki.jp
+kawatana.nagasaki.jp
+kuchinotsu.nagasaki.jp
+matsuura.nagasaki.jp
+nagasaki.nagasaki.jp
+obama.nagasaki.jp
+omura.nagasaki.jp
+oseto.nagasaki.jp
+saikai.nagasaki.jp
+sasebo.nagasaki.jp
+seihi.nagasaki.jp
+shimabara.nagasaki.jp
+shinkamigoto.nagasaki.jp
+togitsu.nagasaki.jp
+tsushima.nagasaki.jp
+unzen.nagasaki.jp
+ando.nara.jp
+gose.nara.jp
+heguri.nara.jp
+higashiyoshino.nara.jp
+ikaruga.nara.jp
+ikoma.nara.jp
+kamikitayama.nara.jp
+kanmaki.nara.jp
+kashiba.nara.jp
+kashihara.nara.jp
+katsuragi.nara.jp
+kawai.nara.jp
+kawakami.nara.jp
+kawanishi.nara.jp
+koryo.nara.jp
+kurotaki.nara.jp
+mitsue.nara.jp
+miyake.nara.jp
+nara.nara.jp
+nosegawa.nara.jp
+oji.nara.jp
+ouda.nara.jp
+oyodo.nara.jp
+sakurai.nara.jp
+sango.nara.jp
+shimoichi.nara.jp
+shimokitayama.nara.jp
+shinjo.nara.jp
+soni.nara.jp
+takatori.nara.jp
+tawaramoto.nara.jp
+tenkawa.nara.jp
+tenri.nara.jp
+uda.nara.jp
+yamatokoriyama.nara.jp
+yamatotakada.nara.jp
+yamazoe.nara.jp
+yoshino.nara.jp
+aga.niigata.jp
+agano.niigata.jp
+gosen.niigata.jp
+itoigawa.niigata.jp
+izumozaki.niigata.jp
+joetsu.niigata.jp
+kamo.niigata.jp
+kariwa.niigata.jp
+kashiwazaki.niigata.jp
+minamiuonuma.niigata.jp
+mitsuke.niigata.jp
+muika.niigata.jp
+murakami.niigata.jp
+myoko.niigata.jp
+nagaoka.niigata.jp
+niigata.niigata.jp
+ojiya.niigata.jp
+omi.niigata.jp
+sado.niigata.jp
+sanjo.niigata.jp
+seiro.niigata.jp
+seirou.niigata.jp
+sekikawa.niigata.jp
+shibata.niigata.jp
+tagami.niigata.jp
+tainai.niigata.jp
+tochio.niigata.jp
+tokamachi.niigata.jp
+tsubame.niigata.jp
+tsunan.niigata.jp
+uonuma.niigata.jp
+yahiko.niigata.jp
+yoita.niigata.jp
+yuzawa.niigata.jp
+beppu.oita.jp
+bungoono.oita.jp
+bungotakada.oita.jp
+hasama.oita.jp
+hiji.oita.jp
+himeshima.oita.jp
+hita.oita.jp
+kamitsue.oita.jp
+kokonoe.oita.jp
+kuju.oita.jp
+kunisaki.oita.jp
+kusu.oita.jp
+oita.oita.jp
+saiki.oita.jp
+taketa.oita.jp
+tsukumi.oita.jp
+usa.oita.jp
+usuki.oita.jp
+yufu.oita.jp
+akaiwa.okayama.jp
+asakuchi.okayama.jp
+bizen.okayama.jp
+hayashima.okayama.jp
+ibara.okayama.jp
+kagamino.okayama.jp
+kasaoka.okayama.jp
+kibichuo.okayama.jp
+kumenan.okayama.jp
+kurashiki.okayama.jp
+maniwa.okayama.jp
+misaki.okayama.jp
+nagi.okayama.jp
+niimi.okayama.jp
+nishiawakura.okayama.jp
+okayama.okayama.jp
+satosho.okayama.jp
+setouchi.okayama.jp
+shinjo.okayama.jp
+shoo.okayama.jp
+soja.okayama.jp
+takahashi.okayama.jp
+tamano.okayama.jp
+tsuyama.okayama.jp
+wake.okayama.jp
+yakage.okayama.jp
+aguni.okinawa.jp
+ginowan.okinawa.jp
+ginoza.okinawa.jp
+gushikami.okinawa.jp
+haebaru.okinawa.jp
+higashi.okinawa.jp
+hirara.okinawa.jp
+iheya.okinawa.jp
+ishigaki.okinawa.jp
+ishikawa.okinawa.jp
+itoman.okinawa.jp
+izena.okinawa.jp
+kadena.okinawa.jp
+kin.okinawa.jp
+kitadaito.okinawa.jp
+kitanakagusuku.okinawa.jp
+kumejima.okinawa.jp
+kunigami.okinawa.jp
+minamidaito.okinawa.jp
+motobu.okinawa.jp
+nago.okinawa.jp
+naha.okinawa.jp
+nakagusuku.okinawa.jp
+nakijin.okinawa.jp
+nanjo.okinawa.jp
+nishihara.okinawa.jp
+ogimi.okinawa.jp
+okinawa.okinawa.jp
+onna.okinawa.jp
+shimoji.okinawa.jp
+taketomi.okinawa.jp
+tarama.okinawa.jp
+tokashiki.okinawa.jp
+tomigusuku.okinawa.jp
+tonaki.okinawa.jp
+urasoe.okinawa.jp
+uruma.okinawa.jp
+yaese.okinawa.jp
+yomitan.okinawa.jp
+yonabaru.okinawa.jp
+yonaguni.okinawa.jp
+zamami.okinawa.jp
+abeno.osaka.jp
+chihayaakasaka.osaka.jp
+chuo.osaka.jp
+daito.osaka.jp
+fujiidera.osaka.jp
+habikino.osaka.jp
+hannan.osaka.jp
+higashiosaka.osaka.jp
+higashisumiyoshi.osaka.jp
+higashiyodogawa.osaka.jp
+hirakata.osaka.jp
+ibaraki.osaka.jp
+ikeda.osaka.jp
+izumi.osaka.jp
+izumiotsu.osaka.jp
+izumisano.osaka.jp
+kadoma.osaka.jp
+kaizuka.osaka.jp
+kanan.osaka.jp
+kashiwara.osaka.jp
+katano.osaka.jp
+kawachinagano.osaka.jp
+kishiwada.osaka.jp
+kita.osaka.jp
+kumatori.osaka.jp
+matsubara.osaka.jp
+minato.osaka.jp
+minoh.osaka.jp
+misaki.osaka.jp
+moriguchi.osaka.jp
+neyagawa.osaka.jp
+nishi.osaka.jp
+nose.osaka.jp
+osakasayama.osaka.jp
+sakai.osaka.jp
+sayama.osaka.jp
+sennan.osaka.jp
+settsu.osaka.jp
+shijonawate.osaka.jp
+shimamoto.osaka.jp
+suita.osaka.jp
+tadaoka.osaka.jp
+taishi.osaka.jp
+tajiri.osaka.jp
+takaishi.osaka.jp
+takatsuki.osaka.jp
+tondabayashi.osaka.jp
+toyonaka.osaka.jp
+toyono.osaka.jp
+yao.osaka.jp
+ariake.saga.jp
+arita.saga.jp
+fukudomi.saga.jp
+genkai.saga.jp
+hamatama.saga.jp
+hizen.saga.jp
+imari.saga.jp
+kamimine.saga.jp
+kanzaki.saga.jp
+karatsu.saga.jp
+kashima.saga.jp
+kitagata.saga.jp
+kitahata.saga.jp
+kiyama.saga.jp
+kouhoku.saga.jp
+kyuragi.saga.jp
+nishiarita.saga.jp
+ogi.saga.jp
+omachi.saga.jp
+ouchi.saga.jp
+saga.saga.jp
+shiroishi.saga.jp
+taku.saga.jp
+tara.saga.jp
+tosu.saga.jp
+yoshinogari.saga.jp
+arakawa.saitama.jp
+asaka.saitama.jp
+chichibu.saitama.jp
+fujimi.saitama.jp
+fujimino.saitama.jp
+fukaya.saitama.jp
+hanno.saitama.jp
+hanyu.saitama.jp
+hasuda.saitama.jp
+hatogaya.saitama.jp
+hatoyama.saitama.jp
+hidaka.saitama.jp
+higashichichibu.saitama.jp
+higashimatsuyama.saitama.jp
+honjo.saitama.jp
+ina.saitama.jp
+iruma.saitama.jp
+iwatsuki.saitama.jp
+kamiizumi.saitama.jp
+kamikawa.saitama.jp
+kamisato.saitama.jp
+kasukabe.saitama.jp
+kawagoe.saitama.jp
+kawaguchi.saitama.jp
+kawajima.saitama.jp
+kazo.saitama.jp
+kitamoto.saitama.jp
+koshigaya.saitama.jp
+kounosu.saitama.jp
+kuki.saitama.jp
+kumagaya.saitama.jp
+matsubushi.saitama.jp
+minano.saitama.jp
+misato.saitama.jp
+miyashiro.saitama.jp
+miyoshi.saitama.jp
+moroyama.saitama.jp
+nagatoro.saitama.jp
+namegawa.saitama.jp
+niiza.saitama.jp
+ogano.saitama.jp
+ogawa.saitama.jp
+ogose.saitama.jp
+okegawa.saitama.jp
+omiya.saitama.jp
+otaki.saitama.jp
+ranzan.saitama.jp
+ryokami.saitama.jp
+saitama.saitama.jp
+sakado.saitama.jp
+satte.saitama.jp
+sayama.saitama.jp
+shiki.saitama.jp
+shiraoka.saitama.jp
+soka.saitama.jp
+sugito.saitama.jp
+toda.saitama.jp
+tokigawa.saitama.jp
+tokorozawa.saitama.jp
+tsurugashima.saitama.jp
+urawa.saitama.jp
+warabi.saitama.jp
+yashio.saitama.jp
+yokoze.saitama.jp
+yono.saitama.jp
+yorii.saitama.jp
+yoshida.saitama.jp
+yoshikawa.saitama.jp
+yoshimi.saitama.jp
+aisho.shiga.jp
+gamo.shiga.jp
+higashiomi.shiga.jp
+hikone.shiga.jp
+koka.shiga.jp
+konan.shiga.jp
+kosei.shiga.jp
+koto.shiga.jp
+kusatsu.shiga.jp
+maibara.shiga.jp
+moriyama.shiga.jp
+nagahama.shiga.jp
+nishiazai.shiga.jp
+notogawa.shiga.jp
+omihachiman.shiga.jp
+otsu.shiga.jp
+ritto.shiga.jp
+ryuoh.shiga.jp
+takashima.shiga.jp
+takatsuki.shiga.jp
+torahime.shiga.jp
+toyosato.shiga.jp
+yasu.shiga.jp
+akagi.shimane.jp
+ama.shimane.jp
+gotsu.shimane.jp
+hamada.shimane.jp
+higashiizumo.shimane.jp
+hikawa.shimane.jp
+hikimi.shimane.jp
+izumo.shimane.jp
+kakinoki.shimane.jp
+masuda.shimane.jp
+matsue.shimane.jp
+misato.shimane.jp
+nishinoshima.shimane.jp
+ohda.shimane.jp
+okinoshima.shimane.jp
+okuizumo.shimane.jp
+shimane.shimane.jp
+tamayu.shimane.jp
+tsuwano.shimane.jp
+unnan.shimane.jp
+yakumo.shimane.jp
+yasugi.shimane.jp
+yatsuka.shimane.jp
+arai.shizuoka.jp
+atami.shizuoka.jp
+fuji.shizuoka.jp
+fujieda.shizuoka.jp
+fujikawa.shizuoka.jp
+fujinomiya.shizuoka.jp
+fukuroi.shizuoka.jp
+gotemba.shizuoka.jp
+haibara.shizuoka.jp
+hamamatsu.shizuoka.jp
+higashiizu.shizuoka.jp
+ito.shizuoka.jp
+iwata.shizuoka.jp
+izu.shizuoka.jp
+izunokuni.shizuoka.jp
+kakegawa.shizuoka.jp
+kannami.shizuoka.jp
+kawanehon.shizuoka.jp
+kawazu.shizuoka.jp
+kikugawa.shizuoka.jp
+kosai.shizuoka.jp
+makinohara.shizuoka.jp
+matsuzaki.shizuoka.jp
+minamiizu.shizuoka.jp
+mishima.shizuoka.jp
+morimachi.shizuoka.jp
+nishiizu.shizuoka.jp
+numazu.shizuoka.jp
+omaezaki.shizuoka.jp
+shimada.shizuoka.jp
+shimizu.shizuoka.jp
+shimoda.shizuoka.jp
+shizuoka.shizuoka.jp
+susono.shizuoka.jp
+yaizu.shizuoka.jp
+yoshida.shizuoka.jp
+ashikaga.tochigi.jp
+bato.tochigi.jp
+haga.tochigi.jp
+ichikai.tochigi.jp
+iwafune.tochigi.jp
+kaminokawa.tochigi.jp
+kanuma.tochigi.jp
+karasuyama.tochigi.jp
+kuroiso.tochigi.jp
+mashiko.tochigi.jp
+mibu.tochigi.jp
+moka.tochigi.jp
+motegi.tochigi.jp
+nasu.tochigi.jp
+nasushiobara.tochigi.jp
+nikko.tochigi.jp
+nishikata.tochigi.jp
+nogi.tochigi.jp
+ohira.tochigi.jp
+ohtawara.tochigi.jp
+oyama.tochigi.jp
+sakura.tochigi.jp
+sano.tochigi.jp
+shimotsuke.tochigi.jp
+shioya.tochigi.jp
+takanezawa.tochigi.jp
+tochigi.tochigi.jp
+tsuga.tochigi.jp
+ujiie.tochigi.jp
+utsunomiya.tochigi.jp
+yaita.tochigi.jp
+aizumi.tokushima.jp
+anan.tokushima.jp
+ichiba.tokushima.jp
+itano.tokushima.jp
+kainan.tokushima.jp
+komatsushima.tokushima.jp
+matsushige.tokushima.jp
+mima.tokushima.jp
+minami.tokushima.jp
+miyoshi.tokushima.jp
+mugi.tokushima.jp
+nakagawa.tokushima.jp
+naruto.tokushima.jp
+sanagochi.tokushima.jp
+shishikui.tokushima.jp
+tokushima.tokushima.jp
+wajiki.tokushima.jp
+adachi.tokyo.jp
+akiruno.tokyo.jp
+akishima.tokyo.jp
+aogashima.tokyo.jp
+arakawa.tokyo.jp
+bunkyo.tokyo.jp
+chiyoda.tokyo.jp
+chofu.tokyo.jp
+chuo.tokyo.jp
+edogawa.tokyo.jp
+fuchu.tokyo.jp
+fussa.tokyo.jp
+hachijo.tokyo.jp
+hachioji.tokyo.jp
+hamura.tokyo.jp
+higashikurume.tokyo.jp
+higashimurayama.tokyo.jp
+higashiyamato.tokyo.jp
+hino.tokyo.jp
+hinode.tokyo.jp
+hinohara.tokyo.jp
+inagi.tokyo.jp
+itabashi.tokyo.jp
+katsushika.tokyo.jp
+kita.tokyo.jp
+kiyose.tokyo.jp
+kodaira.tokyo.jp
+koganei.tokyo.jp
+kokubunji.tokyo.jp
+komae.tokyo.jp
+koto.tokyo.jp
+kouzushima.tokyo.jp
+kunitachi.tokyo.jp
+machida.tokyo.jp
+meguro.tokyo.jp
+minato.tokyo.jp
+mitaka.tokyo.jp
+mizuho.tokyo.jp
+musashimurayama.tokyo.jp
+musashino.tokyo.jp
+nakano.tokyo.jp
+nerima.tokyo.jp
+ogasawara.tokyo.jp
+okutama.tokyo.jp
+ome.tokyo.jp
+oshima.tokyo.jp
+ota.tokyo.jp
+setagaya.tokyo.jp
+shibuya.tokyo.jp
+shinagawa.tokyo.jp
+shinjuku.tokyo.jp
+suginami.tokyo.jp
+sumida.tokyo.jp
+tachikawa.tokyo.jp
+taito.tokyo.jp
+tama.tokyo.jp
+toshima.tokyo.jp
+chizu.tottori.jp
+hino.tottori.jp
+kawahara.tottori.jp
+koge.tottori.jp
+kotoura.tottori.jp
+misasa.tottori.jp
+nanbu.tottori.jp
+nichinan.tottori.jp
+sakaiminato.tottori.jp
+tottori.tottori.jp
+wakasa.tottori.jp
+yazu.tottori.jp
+yonago.tottori.jp
+asahi.toyama.jp
+fuchu.toyama.jp
+fukumitsu.toyama.jp
+funahashi.toyama.jp
+himi.toyama.jp
+imizu.toyama.jp
+inami.toyama.jp
+johana.toyama.jp
+kamiichi.toyama.jp
+kurobe.toyama.jp
+nakaniikawa.toyama.jp
+namerikawa.toyama.jp
+nanto.toyama.jp
+nyuzen.toyama.jp
+oyabe.toyama.jp
+taira.toyama.jp
+takaoka.toyama.jp
+tateyama.toyama.jp
+toga.toyama.jp
+tonami.toyama.jp
+toyama.toyama.jp
+unazuki.toyama.jp
+uozu.toyama.jp
+yamada.toyama.jp
+arida.wakayama.jp
+aridagawa.wakayama.jp
+gobo.wakayama.jp
+hashimoto.wakayama.jp
+hidaka.wakayama.jp
+hirogawa.wakayama.jp
+inami.wakayama.jp
+iwade.wakayama.jp
+kainan.wakayama.jp
+kamitonda.wakayama.jp
+katsuragi.wakayama.jp
+kimino.wakayama.jp
+kinokawa.wakayama.jp
+kitayama.wakayama.jp
+koya.wakayama.jp
+koza.wakayama.jp
+kozagawa.wakayama.jp
+kudoyama.wakayama.jp
+kushimoto.wakayama.jp
+mihama.wakayama.jp
+misato.wakayama.jp
+nachikatsuura.wakayama.jp
+shingu.wakayama.jp
+shirahama.wakayama.jp
+taiji.wakayama.jp
+tanabe.wakayama.jp
+wakayama.wakayama.jp
+yuasa.wakayama.jp
+yura.wakayama.jp
+asahi.yamagata.jp
+funagata.yamagata.jp
+higashine.yamagata.jp
+iide.yamagata.jp
+kahoku.yamagata.jp
+kaminoyama.yamagata.jp
+kaneyama.yamagata.jp
+kawanishi.yamagata.jp
+mamurogawa.yamagata.jp
+mikawa.yamagata.jp
+murayama.yamagata.jp
+nagai.yamagata.jp
+nakayama.yamagata.jp
+nanyo.yamagata.jp
+nishikawa.yamagata.jp
+obanazawa.yamagata.jp
+oe.yamagata.jp
+oguni.yamagata.jp
+ohkura.yamagata.jp
+oishida.yamagata.jp
+sagae.yamagata.jp
+sakata.yamagata.jp
+sakegawa.yamagata.jp
+shinjo.yamagata.jp
+shirataka.yamagata.jp
+shonai.yamagata.jp
+takahata.yamagata.jp
+tendo.yamagata.jp
+tozawa.yamagata.jp
+tsuruoka.yamagata.jp
+yamagata.yamagata.jp
+yamanobe.yamagata.jp
+yonezawa.yamagata.jp
+yuza.yamagata.jp
+abu.yamaguchi.jp
+hagi.yamaguchi.jp
+hikari.yamaguchi.jp
+hofu.yamaguchi.jp
+iwakuni.yamaguchi.jp
+kudamatsu.yamaguchi.jp
+mitou.yamaguchi.jp
+nagato.yamaguchi.jp
+oshima.yamaguchi.jp
+shimonoseki.yamaguchi.jp
+shunan.yamaguchi.jp
+tabuse.yamaguchi.jp
+tokuyama.yamaguchi.jp
+toyota.yamaguchi.jp
+ube.yamaguchi.jp
+yuu.yamaguchi.jp
+chuo.yamanashi.jp
+doshi.yamanashi.jp
+fuefuki.yamanashi.jp
+fujikawa.yamanashi.jp
+fujikawaguchiko.yamanashi.jp
+fujiyoshida.yamanashi.jp
+hayakawa.yamanashi.jp
+hokuto.yamanashi.jp
+ichikawamisato.yamanashi.jp
+kai.yamanashi.jp
+kofu.yamanashi.jp
+koshu.yamanashi.jp
+kosuge.yamanashi.jp
+minami-alps.yamanashi.jp
+minobu.yamanashi.jp
+nakamichi.yamanashi.jp
+nanbu.yamanashi.jp
+narusawa.yamanashi.jp
+nirasaki.yamanashi.jp
+nishikatsura.yamanashi.jp
+oshino.yamanashi.jp
+otsuki.yamanashi.jp
+showa.yamanashi.jp
+tabayama.yamanashi.jp
+tsuru.yamanashi.jp
+uenohara.yamanashi.jp
+yamanakako.yamanashi.jp
+yamanashi.yamanashi.jp
+
+// ke : http://www.kenic.or.ke/index.php?option=com_content&task=view&id=117&Itemid=145
+*.ke
+
+// kg : http://www.domain.kg/dmn_n.html
+kg
+org.kg
+net.kg
+com.kg
+edu.kg
+gov.kg
+mil.kg
+
+// kh : http://www.mptc.gov.kh/dns_registration.htm
+*.kh
+
+// ki : http://www.ki/dns/index.html
+ki
+edu.ki
+biz.ki
+net.ki
+org.ki
+gov.ki
+info.ki
+com.ki
+
+// km : http://en.wikipedia.org/wiki/.km
+// http://www.domaine.km/documents/charte.doc
+km
+org.km
+nom.km
+gov.km
+prd.km
+tm.km
+edu.km
+mil.km
+ass.km
+com.km
+// These are only mentioned as proposed suggestions at domaine.km, but
+// http://en.wikipedia.org/wiki/.km says they're available for registration:
+coop.km
+asso.km
+presse.km
+medecin.km
+notaires.km
+pharmaciens.km
+veterinaire.km
+gouv.km
+
+// kn : http://en.wikipedia.org/wiki/.kn
+// http://www.dot.kn/domainRules.html
+kn
+net.kn
+org.kn
+edu.kn
+gov.kn
+
+// kp : http://www.kcce.kp/en_index.php
+kp
+com.kp
+edu.kp
+gov.kp
+org.kp
+rep.kp
+tra.kp
+
+// kr : http://en.wikipedia.org/wiki/.kr
+// see also: http://domain.nida.or.kr/eng/registration.jsp
+kr
+ac.kr
+co.kr
+es.kr
+go.kr
+hs.kr
+kg.kr
+mil.kr
+ms.kr
+ne.kr
+or.kr
+pe.kr
+re.kr
+sc.kr
+// kr geographical names
+busan.kr
+chungbuk.kr
+chungnam.kr
+daegu.kr
+daejeon.kr
+gangwon.kr
+gwangju.kr
+gyeongbuk.kr
+gyeonggi.kr
+gyeongnam.kr
+incheon.kr
+jeju.kr
+jeonbuk.kr
+jeonnam.kr
+seoul.kr
+ulsan.kr
+
+// kw : http://en.wikipedia.org/wiki/.kw
+*.kw
+
+// ky : http://www.icta.ky/da_ky_reg_dom.php
+// Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17
+ky
+edu.ky
+gov.ky
+com.ky
+org.ky
+net.ky
+
+// kz : http://en.wikipedia.org/wiki/.kz
+// see also: http://www.nic.kz/rules/index.jsp
+kz
+org.kz
+edu.kz
+net.kz
+gov.kz
+mil.kz
+com.kz
+
+// la : http://en.wikipedia.org/wiki/.la
+// Submitted by registry <gavin.brown@nic.la> 2008-06-10
+la
+int.la
+net.la
+info.la
+edu.la
+gov.la
+per.la
+com.la
+org.la
+
+// lb : http://en.wikipedia.org/wiki/.lb
+// Submitted by registry <randy@psg.com> 2008-06-17
+lb
+com.lb
+edu.lb
+gov.lb
+net.lb
+org.lb
+
+// lc : http://en.wikipedia.org/wiki/.lc
+// see also: http://www.nic.lc/rules.htm
+lc
+com.lc
+net.lc
+co.lc
+org.lc
+edu.lc
+gov.lc
+
+// li : http://en.wikipedia.org/wiki/.li
+li
+
+// lk : http://www.nic.lk/seclevpr.html
+lk
+gov.lk
+sch.lk
+net.lk
+int.lk
+com.lk
+org.lk
+edu.lk
+ngo.lk
+soc.lk
+web.lk
+ltd.lk
+assn.lk
+grp.lk
+hotel.lk
+
+// lr : http://psg.com/dns/lr/lr.txt
+// Submitted by registry <randy@psg.com> 2008-06-17
+lr
+com.lr
+edu.lr
+gov.lr
+org.lr
+net.lr
+
+// ls : http://en.wikipedia.org/wiki/.ls
+ls
+co.ls
+org.ls
+
+// lt : http://en.wikipedia.org/wiki/.lt
+lt
+// gov.lt : http://www.gov.lt/index_en.php
+gov.lt
+
+// lu : http://www.dns.lu/en/
+lu
+
+// lv : http://www.nic.lv/DNS/En/generic.php
+lv
+com.lv
+edu.lv
+gov.lv
+org.lv
+mil.lv
+id.lv
+net.lv
+asn.lv
+conf.lv
+
+// ly : http://www.nic.ly/regulations.php
+ly
+com.ly
+net.ly
+gov.ly
+plc.ly
+edu.ly
+sch.ly
+med.ly
+org.ly
+id.ly
+
+// ma : http://en.wikipedia.org/wiki/.ma
+// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf
+ma
+co.ma
+net.ma
+gov.ma
+org.ma
+ac.ma
+press.ma
+
+// mc : http://www.nic.mc/
+mc
+tm.mc
+asso.mc
+
+// md : http://en.wikipedia.org/wiki/.md
+md
+
+// me : http://en.wikipedia.org/wiki/.me
+me
+co.me
+net.me
+org.me
+edu.me
+ac.me
+gov.me
+its.me
+priv.me
+
+// mg : http://www.nic.mg/tarif.htm
+mg
+org.mg
+nom.mg
+gov.mg
+prd.mg
+tm.mg
+edu.mg
+mil.mg
+com.mg
+
+// mh : http://en.wikipedia.org/wiki/.mh
+mh
+
+// mil : http://en.wikipedia.org/wiki/.mil
+mil
+
+// mk : http://en.wikipedia.org/wiki/.mk
+// see also: http://dns.marnet.net.mk/postapka.php
+mk
+com.mk
+org.mk
+net.mk
+edu.mk
+gov.mk
+inf.mk
+name.mk
+
+// ml : http://www.gobin.info/domainname/ml-template.doc
+// see also: http://en.wikipedia.org/wiki/.ml
+ml
+com.ml
+edu.ml
+gouv.ml
+gov.ml
+net.ml
+org.ml
+presse.ml
+
+// mm : http://en.wikipedia.org/wiki/.mm
+*.mm
+
+// mn : http://en.wikipedia.org/wiki/.mn
+mn
+gov.mn
+edu.mn
+org.mn
+
+// mo : http://www.monic.net.mo/
+mo
+com.mo
+net.mo
+org.mo
+edu.mo
+gov.mo
+
+// mobi : http://en.wikipedia.org/wiki/.mobi
+mobi
+
+// mp : http://www.dot.mp/
+// Confirmed by registry <dcamacho@saipan.com> 2008-06-17
+mp
+
+// mq : http://en.wikipedia.org/wiki/.mq
+mq
+
+// mr : http://en.wikipedia.org/wiki/.mr
+mr
+gov.mr
+
+// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf
+ms
+com.ms
+edu.ms
+gov.ms
+net.ms
+org.ms
+
+// mt : https://www.nic.org.mt/go/policy
+// Submitted by registry <help@nic.org.mt> 2013-11-19
+mt
+com.mt
+edu.mt
+net.mt
+org.mt
+
+// mu : http://en.wikipedia.org/wiki/.mu
+mu
+com.mu
+net.mu
+org.mu
+gov.mu
+ac.mu
+co.mu
+or.mu
+
+// museum : http://about.museum/naming/
+// http://index.museum/
+museum
+academy.museum
+agriculture.museum
+air.museum
+airguard.museum
+alabama.museum
+alaska.museum
+amber.museum
+ambulance.museum
+american.museum
+americana.museum
+americanantiques.museum
+americanart.museum
+amsterdam.museum
+and.museum
+annefrank.museum
+anthro.museum
+anthropology.museum
+antiques.museum
+aquarium.museum
+arboretum.museum
+archaeological.museum
+archaeology.museum
+architecture.museum
+art.museum
+artanddesign.museum
+artcenter.museum
+artdeco.museum
+arteducation.museum
+artgallery.museum
+arts.museum
+artsandcrafts.museum
+asmatart.museum
+assassination.museum
+assisi.museum
+association.museum
+astronomy.museum
+atlanta.museum
+austin.museum
+australia.museum
+automotive.museum
+aviation.museum
+axis.museum
+badajoz.museum
+baghdad.museum
+bahn.museum
+bale.museum
+baltimore.museum
+barcelona.museum
+baseball.museum
+basel.museum
+baths.museum
+bauern.museum
+beauxarts.museum
+beeldengeluid.museum
+bellevue.museum
+bergbau.museum
+berkeley.museum
+berlin.museum
+bern.museum
+bible.museum
+bilbao.museum
+bill.museum
+birdart.museum
+birthplace.museum
+bonn.museum
+boston.museum
+botanical.museum
+botanicalgarden.museum
+botanicgarden.museum
+botany.museum
+brandywinevalley.museum
+brasil.museum
+bristol.museum
+british.museum
+britishcolumbia.museum
+broadcast.museum
+brunel.museum
+brussel.museum
+brussels.museum
+bruxelles.museum
+building.museum
+burghof.museum
+bus.museum
+bushey.museum
+cadaques.museum
+california.museum
+cambridge.museum
+can.museum
+canada.museum
+capebreton.museum
+carrier.museum
+cartoonart.museum
+casadelamoneda.museum
+castle.museum
+castres.museum
+celtic.museum
+center.museum
+chattanooga.museum
+cheltenham.museum
+chesapeakebay.museum
+chicago.museum
+children.museum
+childrens.museum
+childrensgarden.museum
+chiropractic.museum
+chocolate.museum
+christiansburg.museum
+cincinnati.museum
+cinema.museum
+circus.museum
+civilisation.museum
+civilization.museum
+civilwar.museum
+clinton.museum
+clock.museum
+coal.museum
+coastaldefence.museum
+cody.museum
+coldwar.museum
+collection.museum
+colonialwilliamsburg.museum
+coloradoplateau.museum
+columbia.museum
+columbus.museum
+communication.museum
+communications.museum
+community.museum
+computer.museum
+computerhistory.museum
+comunicações.museum
+contemporary.museum
+contemporaryart.museum
+convent.museum
+copenhagen.museum
+corporation.museum
+correios-e-telecomunicações.museum
+corvette.museum
+costume.museum
+countryestate.museum
+county.museum
+crafts.museum
+cranbrook.museum
+creation.museum
+cultural.museum
+culturalcenter.museum
+culture.museum
+cyber.museum
+cymru.museum
+dali.museum
+dallas.museum
+database.museum
+ddr.museum
+decorativearts.museum
+delaware.museum
+delmenhorst.museum
+denmark.museum
+depot.museum
+design.museum
+detroit.museum
+dinosaur.museum
+discovery.museum
+dolls.museum
+donostia.museum
+durham.museum
+eastafrica.museum
+eastcoast.museum
+education.museum
+educational.museum
+egyptian.museum
+eisenbahn.museum
+elburg.museum
+elvendrell.museum
+embroidery.museum
+encyclopedic.museum
+england.museum
+entomology.museum
+environment.museum
+environmentalconservation.museum
+epilepsy.museum
+essex.museum
+estate.museum
+ethnology.museum
+exeter.museum
+exhibition.museum
+family.museum
+farm.museum
+farmequipment.museum
+farmers.museum
+farmstead.museum
+field.museum
+figueres.museum
+filatelia.museum
+film.museum
+fineart.museum
+finearts.museum
+finland.museum
+flanders.museum
+florida.museum
+force.museum
+fortmissoula.museum
+fortworth.museum
+foundation.museum
+francaise.museum
+frankfurt.museum
+franziskaner.museum
+freemasonry.museum
+freiburg.museum
+fribourg.museum
+frog.museum
+fundacio.museum
+furniture.museum
+gallery.museum
+garden.museum
+gateway.museum
+geelvinck.museum
+gemological.museum
+geology.museum
+georgia.museum
+giessen.museum
+glas.museum
+glass.museum
+gorge.museum
+grandrapids.museum
+graz.museum
+guernsey.museum
+halloffame.museum
+hamburg.museum
+handson.museum
+harvestcelebration.museum
+hawaii.museum
+health.museum
+heimatunduhren.museum
+hellas.museum
+helsinki.museum
+hembygdsforbund.museum
+heritage.museum
+histoire.museum
+historical.museum
+historicalsociety.museum
+historichouses.museum
+historisch.museum
+historisches.museum
+history.museum
+historyofscience.museum
+horology.museum
+house.museum
+humanities.museum
+illustration.museum
+imageandsound.museum
+indian.museum
+indiana.museum
+indianapolis.museum
+indianmarket.museum
+intelligence.museum
+interactive.museum
+iraq.museum
+iron.museum
+isleofman.museum
+jamison.museum
+jefferson.museum
+jerusalem.museum
+jewelry.museum
+jewish.museum
+jewishart.museum
+jfk.museum
+journalism.museum
+judaica.museum
+judygarland.museum
+juedisches.museum
+juif.museum
+karate.museum
+karikatur.museum
+kids.museum
+koebenhavn.museum
+koeln.museum
+kunst.museum
+kunstsammlung.museum
+kunstunddesign.museum
+labor.museum
+labour.museum
+lajolla.museum
+lancashire.museum
+landes.museum
+lans.museum
+läns.museum
+larsson.museum
+lewismiller.museum
+lincoln.museum
+linz.museum
+living.museum
+livinghistory.museum
+localhistory.museum
+london.museum
+losangeles.museum
+louvre.museum
+loyalist.museum
+lucerne.museum
+luxembourg.museum
+luzern.museum
+mad.museum
+madrid.museum
+mallorca.museum
+manchester.museum
+mansion.museum
+mansions.museum
+manx.museum
+marburg.museum
+maritime.museum
+maritimo.museum
+maryland.museum
+marylhurst.museum
+media.museum
+medical.museum
+medizinhistorisches.museum
+meeres.museum
+memorial.museum
+mesaverde.museum
+michigan.museum
+midatlantic.museum
+military.museum
+mill.museum
+miners.museum
+mining.museum
+minnesota.museum
+missile.museum
+missoula.museum
+modern.museum
+moma.museum
+money.museum
+monmouth.museum
+monticello.museum
+montreal.museum
+moscow.museum
+motorcycle.museum
+muenchen.museum
+muenster.museum
+mulhouse.museum
+muncie.museum
+museet.museum
+museumcenter.museum
+museumvereniging.museum
+music.museum
+national.museum
+nationalfirearms.museum
+nationalheritage.museum
+nativeamerican.museum
+naturalhistory.museum
+naturalhistorymuseum.museum
+naturalsciences.museum
+nature.museum
+naturhistorisches.museum
+natuurwetenschappen.museum
+naumburg.museum
+naval.museum
+nebraska.museum
+neues.museum
+newhampshire.museum
+newjersey.museum
+newmexico.museum
+newport.museum
+newspaper.museum
+newyork.museum
+niepce.museum
+norfolk.museum
+north.museum
+nrw.museum
+nuernberg.museum
+nuremberg.museum
+nyc.museum
+nyny.museum
+oceanographic.museum
+oceanographique.museum
+omaha.museum
+online.museum
+ontario.museum
+openair.museum
+oregon.museum
+oregontrail.museum
+otago.museum
+oxford.museum
+pacific.museum
+paderborn.museum
+palace.museum
+paleo.museum
+palmsprings.museum
+panama.museum
+paris.museum
+pasadena.museum
+pharmacy.museum
+philadelphia.museum
+philadelphiaarea.museum
+philately.museum
+phoenix.museum
+photography.museum
+pilots.museum
+pittsburgh.museum
+planetarium.museum
+plantation.museum
+plants.museum
+plaza.museum
+portal.museum
+portland.museum
+portlligat.museum
+posts-and-telecommunications.museum
+preservation.museum
+presidio.museum
+press.museum
+project.museum
+public.museum
+pubol.museum
+quebec.museum
+railroad.museum
+railway.museum
+research.museum
+resistance.museum
+riodejaneiro.museum
+rochester.museum
+rockart.museum
+roma.museum
+russia.museum
+saintlouis.museum
+salem.museum
+salvadordali.museum
+salzburg.museum
+sandiego.museum
+sanfrancisco.museum
+santabarbara.museum
+santacruz.museum
+santafe.museum
+saskatchewan.museum
+satx.museum
+savannahga.museum
+schlesisches.museum
+schoenbrunn.museum
+schokoladen.museum
+school.museum
+schweiz.museum
+science.museum
+scienceandhistory.museum
+scienceandindustry.museum
+sciencecenter.museum
+sciencecenters.museum
+science-fiction.museum
+sciencehistory.museum
+sciences.museum
+sciencesnaturelles.museum
+scotland.museum
+seaport.museum
+settlement.museum
+settlers.museum
+shell.museum
+sherbrooke.museum
+sibenik.museum
+silk.museum
+ski.museum
+skole.museum
+society.museum
+sologne.museum
+soundandvision.museum
+southcarolina.museum
+southwest.museum
+space.museum
+spy.museum
+square.museum
+stadt.museum
+stalbans.museum
+starnberg.museum
+state.museum
+stateofdelaware.museum
+station.museum
+steam.museum
+steiermark.museum
+stjohn.museum
+stockholm.museum
+stpetersburg.museum
+stuttgart.museum
+suisse.museum
+surgeonshall.museum
+surrey.museum
+svizzera.museum
+sweden.museum
+sydney.museum
+tank.museum
+tcm.museum
+technology.museum
+telekommunikation.museum
+television.museum
+texas.museum
+textile.museum
+theater.museum
+time.museum
+timekeeping.museum
+topology.museum
+torino.museum
+touch.museum
+town.museum
+transport.museum
+tree.museum
+trolley.museum
+trust.museum
+trustee.museum
+uhren.museum
+ulm.museum
+undersea.museum
+university.museum
+usa.museum
+usantiques.museum
+usarts.museum
+uscountryestate.museum
+usculture.museum
+usdecorativearts.museum
+usgarden.museum
+ushistory.museum
+ushuaia.museum
+uslivinghistory.museum
+utah.museum
+uvic.museum
+valley.museum
+vantaa.museum
+versailles.museum
+viking.museum
+village.museum
+virginia.museum
+virtual.museum
+virtuel.museum
+vlaanderen.museum
+volkenkunde.museum
+wales.museum
+wallonie.museum
+war.museum
+washingtondc.museum
+watchandclock.museum
+watch-and-clock.museum
+western.museum
+westfalen.museum
+whaling.museum
+wildlife.museum
+williamsburg.museum
+windmill.museum
+workshop.museum
+york.museum
+yorkshire.museum
+yosemite.museum
+youth.museum
+zoological.museum
+zoology.museum
+ירושלים.museum
+иком.museum
+
+// mv : http://en.wikipedia.org/wiki/.mv
+// "mv" included because, contra Wikipedia, google.mv exists.
+mv
+aero.mv
+biz.mv
+com.mv
+coop.mv
+edu.mv
+gov.mv
+info.mv
+int.mv
+mil.mv
+museum.mv
+name.mv
+net.mv
+org.mv
+pro.mv
+
+// mw : http://www.registrar.mw/
+mw
+ac.mw
+biz.mw
+co.mw
+com.mw
+coop.mw
+edu.mw
+gov.mw
+int.mw
+museum.mw
+net.mw
+org.mw
+
+// mx : http://www.nic.mx/
+// Submitted by registry <farias@nic.mx> 2008-06-19
+mx
+com.mx
+org.mx
+gob.mx
+edu.mx
+net.mx
+
+// my : http://www.mynic.net.my/
+my
+com.my
+net.my
+org.my
+gov.my
+edu.my
+mil.my
+name.my
+
+// mz : http://www.gobin.info/domainname/mz-template.doc
+*.mz
+!teledata.mz
+
+// na : http://www.na-nic.com.na/
+// http://www.info.na/domain/
+na
+info.na
+pro.na
+name.na
+school.na
+or.na
+dr.na
+us.na
+mx.na
+ca.na
+in.na
+cc.na
+tv.na
+ws.na
+mobi.na
+co.na
+com.na
+org.na
+
+// name : has 2nd-level tlds, but there's no list of them
+name
+
+// nc : http://www.cctld.nc/
+nc
+asso.nc
+
+// ne : http://en.wikipedia.org/wiki/.ne
+ne
+
+// net : http://en.wikipedia.org/wiki/.net
+net
+
+// nf : http://en.wikipedia.org/wiki/.nf
+nf
+com.nf
+net.nf
+per.nf
+rec.nf
+web.nf
+arts.nf
+firm.nf
+info.nf
+other.nf
+store.nf
+
+// ng : http://psg.com/dns/ng/
+ng
+com.ng
+edu.ng
+name.ng
+net.ng
+org.ng
+sch.ng
+gov.ng
+mil.ng
+mobi.ng
+
+// ni : http://www.nic.ni/dominios.htm
+*.ni
+
+// nl : http://www.domain-registry.nl/ace.php/c,728,122,,,,Home.html
+// Confirmed by registry <Antoin.Verschuren@sidn.nl> (with technical
+// reservations) 2008-06-08
+nl
+
+// BV.nl will be a registry for dutch BV's (besloten vennootschap)
+bv.nl
+
+// no : http://www.norid.no/regelverk/index.en.html
+// The Norwegian registry has declined to notify us of updates. The web pages
+// referenced below are the official source of the data. There is also an
+// announce mailing list:
+// https://postlister.uninett.no/sympa/info/norid-diskusjon
+no
+// Norid generic domains : http://www.norid.no/regelverk/vedlegg-c.en.html
+fhs.no
+vgs.no
+fylkesbibl.no
+folkebibl.no
+museum.no
+idrett.no
+priv.no
+// Non-Norid generic domains : http://www.norid.no/regelverk/vedlegg-d.en.html
+mil.no
+stat.no
+dep.no
+kommune.no
+herad.no
+// no geographical names : http://www.norid.no/regelverk/vedlegg-b.en.html
+// counties
+aa.no
+ah.no
+bu.no
+fm.no
+hl.no
+hm.no
+jan-mayen.no
+mr.no
+nl.no
+nt.no
+of.no
+ol.no
+oslo.no
+rl.no
+sf.no
+st.no
+svalbard.no
+tm.no
+tr.no
+va.no
+vf.no
+// primary and lower secondary schools per county
+gs.aa.no
+gs.ah.no
+gs.bu.no
+gs.fm.no
+gs.hl.no
+gs.hm.no
+gs.jan-mayen.no
+gs.mr.no
+gs.nl.no
+gs.nt.no
+gs.of.no
+gs.ol.no
+gs.oslo.no
+gs.rl.no
+gs.sf.no
+gs.st.no
+gs.svalbard.no
+gs.tm.no
+gs.tr.no
+gs.va.no
+gs.vf.no
+// cities
+akrehamn.no
+åkrehamn.no
+algard.no
+ålgård.no
+arna.no
+brumunddal.no
+bryne.no
+bronnoysund.no
+brønnøysund.no
+drobak.no
+drøbak.no
+egersund.no
+fetsund.no
+floro.no
+florø.no
+fredrikstad.no
+hokksund.no
+honefoss.no
+hønefoss.no
+jessheim.no
+jorpeland.no
+jørpeland.no
+kirkenes.no
+kopervik.no
+krokstadelva.no
+langevag.no
+langevåg.no
+leirvik.no
+mjondalen.no
+mjøndalen.no
+mo-i-rana.no
+mosjoen.no
+mosjøen.no
+nesoddtangen.no
+orkanger.no
+osoyro.no
+osøyro.no
+raholt.no
+råholt.no
+sandnessjoen.no
+sandnessjøen.no
+skedsmokorset.no
+slattum.no
+spjelkavik.no
+stathelle.no
+stavern.no
+stjordalshalsen.no
+stjørdalshalsen.no
+tananger.no
+tranby.no
+vossevangen.no
+// communities
+afjord.no
+åfjord.no
+agdenes.no
+al.no
+ål.no
+alesund.no
+ålesund.no
+alstahaug.no
+alta.no
+áltá.no
+alaheadju.no
+álaheadju.no
+alvdal.no
+amli.no
+åmli.no
+amot.no
+åmot.no
+andebu.no
+andoy.no
+andøy.no
+andasuolo.no
+ardal.no
+årdal.no
+aremark.no
+arendal.no
+ås.no
+aseral.no
+åseral.no
+asker.no
+askim.no
+askvoll.no
+askoy.no
+askøy.no
+asnes.no
+åsnes.no
+audnedaln.no
+aukra.no
+aure.no
+aurland.no
+aurskog-holand.no
+aurskog-høland.no
+austevoll.no
+austrheim.no
+averoy.no
+averøy.no
+balestrand.no
+ballangen.no
+balat.no
+bálát.no
+balsfjord.no
+bahccavuotna.no
+báhccavuotna.no
+bamble.no
+bardu.no
+beardu.no
+beiarn.no
+bajddar.no
+bájddar.no
+baidar.no
+báidár.no
+berg.no
+bergen.no
+berlevag.no
+berlevåg.no
+bearalvahki.no
+bearalváhki.no
+bindal.no
+birkenes.no
+bjarkoy.no
+bjarkøy.no
+bjerkreim.no
+bjugn.no
+bodo.no
+bodø.no
+badaddja.no
+bådåddjå.no
+budejju.no
+bokn.no
+bremanger.no
+bronnoy.no
+brønnøy.no
+bygland.no
+bykle.no
+barum.no
+bærum.no
+bo.telemark.no
+bø.telemark.no
+bo.nordland.no
+bø.nordland.no
+bievat.no
+bievát.no
+bomlo.no
+bømlo.no
+batsfjord.no
+båtsfjord.no
+bahcavuotna.no
+báhcavuotna.no
+dovre.no
+drammen.no
+drangedal.no
+dyroy.no
+dyrøy.no
+donna.no
+dønna.no
+eid.no
+eidfjord.no
+eidsberg.no
+eidskog.no
+eidsvoll.no
+eigersund.no
+elverum.no
+enebakk.no
+engerdal.no
+etne.no
+etnedal.no
+evenes.no
+evenassi.no
+evenášši.no
+evje-og-hornnes.no
+farsund.no
+fauske.no
+fuossko.no
+fuoisku.no
+fedje.no
+fet.no
+finnoy.no
+finnøy.no
+fitjar.no
+fjaler.no
+fjell.no
+flakstad.no
+flatanger.no
+flekkefjord.no
+flesberg.no
+flora.no
+fla.no
+flå.no
+folldal.no
+forsand.no
+fosnes.no
+frei.no
+frogn.no
+froland.no
+frosta.no
+frana.no
+fræna.no
+froya.no
+frøya.no
+fusa.no
+fyresdal.no
+forde.no
+førde.no
+gamvik.no
+gangaviika.no
+gáŋgaviika.no
+gaular.no
+gausdal.no
+gildeskal.no
+gildeskål.no
+giske.no
+gjemnes.no
+gjerdrum.no
+gjerstad.no
+gjesdal.no
+gjovik.no
+gjøvik.no
+gloppen.no
+gol.no
+gran.no
+grane.no
+granvin.no
+gratangen.no
+grimstad.no
+grong.no
+kraanghke.no
+kråanghke.no
+grue.no
+gulen.no
+hadsel.no
+halden.no
+halsa.no
+hamar.no
+hamaroy.no
+habmer.no
+hábmer.no
+hapmir.no
+hápmir.no
+hammerfest.no
+hammarfeasta.no
+hámmárfeasta.no
+haram.no
+hareid.no
+harstad.no
+hasvik.no
+aknoluokta.no
+ákŋoluokta.no
+hattfjelldal.no
+aarborte.no
+haugesund.no
+hemne.no
+hemnes.no
+hemsedal.no
+heroy.more-og-romsdal.no
+herøy.møre-og-romsdal.no
+heroy.nordland.no
+herøy.nordland.no
+hitra.no
+hjartdal.no
+hjelmeland.no
+hobol.no
+hobøl.no
+hof.no
+hol.no
+hole.no
+holmestrand.no
+holtalen.no
+holtålen.no
+hornindal.no
+horten.no
+hurdal.no
+hurum.no
+hvaler.no
+hyllestad.no
+hagebostad.no
+hægebostad.no
+hoyanger.no
+høyanger.no
+hoylandet.no
+høylandet.no
+ha.no
+hå.no
+ibestad.no
+inderoy.no
+inderøy.no
+iveland.no
+jevnaker.no
+jondal.no
+jolster.no
+jølster.no
+karasjok.no
+karasjohka.no
+kárášjohka.no
+karlsoy.no
+galsa.no
+gálsá.no
+karmoy.no
+karmøy.no
+kautokeino.no
+guovdageaidnu.no
+klepp.no
+klabu.no
+klæbu.no
+kongsberg.no
+kongsvinger.no
+kragero.no
+kragerø.no
+kristiansand.no
+kristiansund.no
+krodsherad.no
+krødsherad.no
+kvalsund.no
+rahkkeravju.no
+ráhkkerávju.no
+kvam.no
+kvinesdal.no
+kvinnherad.no
+kviteseid.no
+kvitsoy.no
+kvitsøy.no
+kvafjord.no
+kvæfjord.no
+giehtavuoatna.no
+kvanangen.no
+kvænangen.no
+navuotna.no
+návuotna.no
+kafjord.no
+kåfjord.no
+gaivuotna.no
+gáivuotna.no
+larvik.no
+lavangen.no
+lavagis.no
+loabat.no
+loabát.no
+lebesby.no
+davvesiida.no
+leikanger.no
+leirfjord.no
+leka.no
+leksvik.no
+lenvik.no
+leangaviika.no
+leaŋgaviika.no
+lesja.no
+levanger.no
+lier.no
+lierne.no
+lillehammer.no
+lillesand.no
+lindesnes.no
+lindas.no
+lindås.no
+lom.no
+loppa.no
+lahppi.no
+láhppi.no
+lund.no
+lunner.no
+luroy.no
+lurøy.no
+luster.no
+lyngdal.no
+lyngen.no
+ivgu.no
+lardal.no
+lerdal.no
+lærdal.no
+lodingen.no
+lødingen.no
+lorenskog.no
+lørenskog.no
+loten.no
+løten.no
+malvik.no
+masoy.no
+måsøy.no
+muosat.no
+muosát.no
+mandal.no
+marker.no
+marnardal.no
+masfjorden.no
+meland.no
+meldal.no
+melhus.no
+meloy.no
+meløy.no
+meraker.no
+meråker.no
+moareke.no
+moåreke.no
+midsund.no
+midtre-gauldal.no
+modalen.no
+modum.no
+molde.no
+moskenes.no
+moss.no
+mosvik.no
+malselv.no
+målselv.no
+malatvuopmi.no
+málatvuopmi.no
+namdalseid.no
+aejrie.no
+namsos.no
+namsskogan.no
+naamesjevuemie.no
+nååmesjevuemie.no
+laakesvuemie.no
+nannestad.no
+narvik.no
+narviika.no
+naustdal.no
+nedre-eiker.no
+nes.akershus.no
+nes.buskerud.no
+nesna.no
+nesodden.no
+nesseby.no
+unjarga.no
+unjárga.no
+nesset.no
+nissedal.no
+nittedal.no
+nord-aurdal.no
+nord-fron.no
+nord-odal.no
+norddal.no
+nordkapp.no
+davvenjarga.no
+davvenjárga.no
+nordre-land.no
+nordreisa.no
+raisa.no
+ráisa.no
+nore-og-uvdal.no
+notodden.no
+naroy.no
+nærøy.no
+notteroy.no
+nøtterøy.no
+odda.no
+oksnes.no
+øksnes.no
+oppdal.no
+oppegard.no
+oppegård.no
+orkdal.no
+orland.no
+ørland.no
+orskog.no
+ørskog.no
+orsta.no
+ørsta.no
+os.hedmark.no
+os.hordaland.no
+osen.no
+osteroy.no
+osterøy.no
+ostre-toten.no
+østre-toten.no
+overhalla.no
+ovre-eiker.no
+øvre-eiker.no
+oyer.no
+øyer.no
+oygarden.no
+øygarden.no
+oystre-slidre.no
+øystre-slidre.no
+porsanger.no
+porsangu.no
+porsáŋgu.no
+porsgrunn.no
+radoy.no
+radøy.no
+rakkestad.no
+rana.no
+ruovat.no
+randaberg.no
+rauma.no
+rendalen.no
+rennebu.no
+rennesoy.no
+rennesøy.no
+rindal.no
+ringebu.no
+ringerike.no
+ringsaker.no
+rissa.no
+risor.no
+risør.no
+roan.no
+rollag.no
+rygge.no
+ralingen.no
+rælingen.no
+rodoy.no
+rødøy.no
+romskog.no
+rømskog.no
+roros.no
+røros.no
+rost.no
+røst.no
+royken.no
+røyken.no
+royrvik.no
+røyrvik.no
+rade.no
+råde.no
+salangen.no
+siellak.no
+saltdal.no
+salat.no
+sálát.no
+sálat.no
+samnanger.no
+sande.more-og-romsdal.no
+sande.møre-og-romsdal.no
+sande.vestfold.no
+sandefjord.no
+sandnes.no
+sandoy.no
+sandøy.no
+sarpsborg.no
+sauda.no
+sauherad.no
+sel.no
+selbu.no
+selje.no
+seljord.no
+sigdal.no
+siljan.no
+sirdal.no
+skaun.no
+skedsmo.no
+ski.no
+skien.no
+skiptvet.no
+skjervoy.no
+skjervøy.no
+skierva.no
+skiervá.no
+skjak.no
+skjåk.no
+skodje.no
+skanland.no
+skånland.no
+skanit.no
+skánit.no
+smola.no
+smøla.no
+snillfjord.no
+snasa.no
+snåsa.no
+snoasa.no
+snaase.no
+snåase.no
+sogndal.no
+sokndal.no
+sola.no
+solund.no
+songdalen.no
+sortland.no
+spydeberg.no
+stange.no
+stavanger.no
+steigen.no
+steinkjer.no
+stjordal.no
+stjørdal.no
+stokke.no
+stor-elvdal.no
+stord.no
+stordal.no
+storfjord.no
+omasvuotna.no
+strand.no
+stranda.no
+stryn.no
+sula.no
+suldal.no
+sund.no
+sunndal.no
+surnadal.no
+sveio.no
+svelvik.no
+sykkylven.no
+sogne.no
+søgne.no
+somna.no
+sømna.no
+sondre-land.no
+søndre-land.no
+sor-aurdal.no
+sør-aurdal.no
+sor-fron.no
+sør-fron.no
+sor-odal.no
+sør-odal.no
+sor-varanger.no
+sør-varanger.no
+matta-varjjat.no
+mátta-várjjat.no
+sorfold.no
+sørfold.no
+sorreisa.no
+sørreisa.no
+sorum.no
+sørum.no
+tana.no
+deatnu.no
+time.no
+tingvoll.no
+tinn.no
+tjeldsund.no
+dielddanuorri.no
+tjome.no
+tjøme.no
+tokke.no
+tolga.no
+torsken.no
+tranoy.no
+tranøy.no
+tromso.no
+tromsø.no
+tromsa.no
+romsa.no
+trondheim.no
+troandin.no
+trysil.no
+trana.no
+træna.no
+trogstad.no
+trøgstad.no
+tvedestrand.no
+tydal.no
+tynset.no
+tysfjord.no
+divtasvuodna.no
+divttasvuotna.no
+tysnes.no
+tysvar.no
+tysvær.no
+tonsberg.no
+tønsberg.no
+ullensaker.no
+ullensvang.no
+ulvik.no
+utsira.no
+vadso.no
+vadsø.no
+cahcesuolo.no
+čáhcesuolo.no
+vaksdal.no
+valle.no
+vang.no
+vanylven.no
+vardo.no
+vardø.no
+varggat.no
+várggát.no
+vefsn.no
+vaapste.no
+vega.no
+vegarshei.no
+vegårshei.no
+vennesla.no
+verdal.no
+verran.no
+vestby.no
+vestnes.no
+vestre-slidre.no
+vestre-toten.no
+vestvagoy.no
+vestvågøy.no
+vevelstad.no
+vik.no
+vikna.no
+vindafjord.no
+volda.no
+voss.no
+varoy.no
+værøy.no
+vagan.no
+vågan.no
+voagat.no
+vagsoy.no
+vågsøy.no
+vaga.no
+vågå.no
+valer.ostfold.no
+våler.østfold.no
+valer.hedmark.no
+våler.hedmark.no
+
+// np : http://www.mos.com.np/register.html
+*.np
+
+// nr : http://cenpac.net.nr/dns/index.html
+// Confirmed by registry <technician@cenpac.net.nr> 2008-06-17
+nr
+biz.nr
+info.nr
+gov.nr
+edu.nr
+org.nr
+net.nr
+com.nr
+
+// nu : http://en.wikipedia.org/wiki/.nu
+nu
+
+// nz : http://en.wikipedia.org/wiki/.nz
+// Confirmed by registry <jay@nzrs.net.nz> 2014-05-19
+nz
+ac.nz
+co.nz
+cri.nz
+geek.nz
+gen.nz
+govt.nz
+health.nz
+iwi.nz
+kiwi.nz
+maori.nz
+mil.nz
+māori.nz
+net.nz
+org.nz
+parliament.nz
+school.nz
+
+// om : http://en.wikipedia.org/wiki/.om
+om
+co.om
+com.om
+edu.om
+gov.om
+med.om
+museum.om
+net.om
+org.om
+pro.om
+
+// org : http://en.wikipedia.org/wiki/.org
+org
+
+// pa : http://www.nic.pa/
+// Some additional second level "domains" resolve directly as hostnames, such as
+// pannet.pa, so we add a rule for "pa".
+pa
+ac.pa
+gob.pa
+com.pa
+org.pa
+sld.pa
+edu.pa
+net.pa
+ing.pa
+abo.pa
+med.pa
+nom.pa
+
+// pe : https://www.nic.pe/InformeFinalComision.pdf
+pe
+edu.pe
+gob.pe
+nom.pe
+mil.pe
+org.pe
+com.pe
+net.pe
+
+// pf : http://www.gobin.info/domainname/formulaire-pf.pdf
+pf
+com.pf
+org.pf
+edu.pf
+
+// pg : http://en.wikipedia.org/wiki/.pg
+*.pg
+
+// ph : http://www.domains.ph/FAQ2.asp
+// Submitted by registry <jed@email.com.ph> 2008-06-13
+ph
+com.ph
+net.ph
+org.ph
+gov.ph
+edu.ph
+ngo.ph
+mil.ph
+i.ph
+
+// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK
+pk
+com.pk
+net.pk
+edu.pk
+org.pk
+fam.pk
+biz.pk
+web.pk
+gov.pk
+gob.pk
+gok.pk
+gon.pk
+gop.pk
+gos.pk
+info.pk
+
+// pl http://www.dns.pl/english/index.html
+// confirmed on 26.09.2014 from Bogna Tchórzewska <partner@dns.pl>
+pl
+com.pl
+net.pl
+org.pl
+info.pl
+waw.pl
+gov.pl
+// pl functional domains (http://www.dns.pl/english/index.html)
+aid.pl
+agro.pl
+atm.pl
+auto.pl
+biz.pl
+edu.pl
+gmina.pl
+gsm.pl
+mail.pl
+miasta.pl
+media.pl
+mil.pl
+nieruchomosci.pl
+nom.pl
+pc.pl
+powiat.pl
+priv.pl
+realestate.pl
+rel.pl
+sex.pl
+shop.pl
+sklep.pl
+sos.pl
+szkola.pl
+targi.pl
+tm.pl
+tourism.pl
+travel.pl
+turystyka.pl
+// Government domains (administred by ippt.gov.pl)
+uw.gov.pl
+um.gov.pl
+ug.gov.pl
+upow.gov.pl
+starostwo.gov.pl
+so.gov.pl
+sr.gov.pl
+po.gov.pl
+pa.gov.pl
+// pl regional domains (http://www.dns.pl/english/index.html)
+augustow.pl
+babia-gora.pl
+bedzin.pl
+beskidy.pl
+bialowieza.pl
+bialystok.pl
+bielawa.pl
+bieszczady.pl
+boleslawiec.pl
+bydgoszcz.pl
+bytom.pl
+cieszyn.pl
+czeladz.pl
+czest.pl
+dlugoleka.pl
+elblag.pl
+elk.pl
+glogow.pl
+gniezno.pl
+gorlice.pl
+grajewo.pl
+ilawa.pl
+jaworzno.pl
+jelenia-gora.pl
+jgora.pl
+kalisz.pl
+kazimierz-dolny.pl
+karpacz.pl
+kartuzy.pl
+kaszuby.pl
+katowice.pl
+kepno.pl
+ketrzyn.pl
+klodzko.pl
+kobierzyce.pl
+kolobrzeg.pl
+konin.pl
+konskowola.pl
+kutno.pl
+lapy.pl
+lebork.pl
+legnica.pl
+lezajsk.pl
+limanowa.pl
+lomza.pl
+lowicz.pl
+lubin.pl
+lukow.pl
+malbork.pl
+malopolska.pl
+mazowsze.pl
+mazury.pl
+mielec.pl
+mielno.pl
+mragowo.pl
+naklo.pl
+nowaruda.pl
+nysa.pl
+olawa.pl
+olecko.pl
+olkusz.pl
+olsztyn.pl
+opoczno.pl
+opole.pl
+ostroda.pl
+ostroleka.pl
+ostrowiec.pl
+ostrowwlkp.pl
+pila.pl
+pisz.pl
+podhale.pl
+podlasie.pl
+polkowice.pl
+pomorze.pl
+pomorskie.pl
+prochowice.pl
+pruszkow.pl
+przeworsk.pl
+pulawy.pl
+radom.pl
+rawa-maz.pl
+rybnik.pl
+rzeszow.pl
+sanok.pl
+sejny.pl
+slask.pl
+slupsk.pl
+sosnowiec.pl
+stalowa-wola.pl
+skoczow.pl
+starachowice.pl
+stargard.pl
+suwalki.pl
+swidnica.pl
+swiebodzin.pl
+swinoujscie.pl
+szczecin.pl
+szczytno.pl
+tarnobrzeg.pl
+tgory.pl
+turek.pl
+tychy.pl
+ustka.pl
+walbrzych.pl
+warmia.pl
+warszawa.pl
+wegrow.pl
+wielun.pl
+wlocl.pl
+wloclawek.pl
+wodzislaw.pl
+wolomin.pl
+wroclaw.pl
+zachpomor.pl
+zagan.pl
+zarow.pl
+zgora.pl
+zgorzelec.pl
+
+// pm : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf
+pm
+
+// pn : http://www.government.pn/PnRegistry/policies.htm
+pn
+gov.pn
+co.pn
+org.pn
+edu.pn
+net.pn
+
+// post : http://en.wikipedia.org/wiki/.post
+post
+
+// pr : http://www.nic.pr/index.asp?f=1
+pr
+com.pr
+net.pr
+org.pr
+gov.pr
+edu.pr
+isla.pr
+pro.pr
+biz.pr
+info.pr
+name.pr
+// these aren't mentioned on nic.pr, but on http://en.wikipedia.org/wiki/.pr
+est.pr
+prof.pr
+ac.pr
+
+// pro : http://www.nic.pro/support_faq.htm
+pro
+aca.pro
+bar.pro
+cpa.pro
+jur.pro
+law.pro
+med.pro
+eng.pro
+
+// ps : http://en.wikipedia.org/wiki/.ps
+// http://www.nic.ps/registration/policy.html#reg
+ps
+edu.ps
+gov.ps
+sec.ps
+plo.ps
+com.ps
+org.ps
+net.ps
+
+// pt : http://online.dns.pt/dns/start_dns
+pt
+net.pt
+gov.pt
+org.pt
+edu.pt
+int.pt
+publ.pt
+com.pt
+nome.pt
+
+// pw : http://en.wikipedia.org/wiki/.pw
+pw
+co.pw
+ne.pw
+or.pw
+ed.pw
+go.pw
+belau.pw
+
+// py : http://www.nic.py/pautas.html#seccion_9
+// Confirmed by registry 2012-10-03
+py
+com.py
+coop.py
+edu.py
+gov.py
+mil.py
+net.py
+org.py
+
+// qa : http://domains.qa/en/
+qa
+com.qa
+edu.qa
+gov.qa
+mil.qa
+name.qa
+net.qa
+org.qa
+sch.qa
+
+// re : http://www.afnic.re/obtenir/chartes/nommage-re/annexe-descriptifs
+re
+com.re
+asso.re
+nom.re
+
+// ro : http://www.rotld.ro/
+ro
+com.ro
+org.ro
+tm.ro
+nt.ro
+nom.ro
+info.ro
+rec.ro
+arts.ro
+firm.ro
+store.ro
+www.ro
+
+// rs : http://en.wikipedia.org/wiki/.rs
+rs
+co.rs
+org.rs
+edu.rs
+ac.rs
+gov.rs
+in.rs
+
+// ru : http://www.cctld.ru/ru/docs/aktiv_8.php
+// Industry domains
+ru
+ac.ru
+com.ru
+edu.ru
+int.ru
+net.ru
+org.ru
+pp.ru
+// Geographical domains
+adygeya.ru
+altai.ru
+amur.ru
+arkhangelsk.ru
+astrakhan.ru
+bashkiria.ru
+belgorod.ru
+bir.ru
+bryansk.ru
+buryatia.ru
+cbg.ru
+chel.ru
+chelyabinsk.ru
+chita.ru
+chukotka.ru
+chuvashia.ru
+dagestan.ru
+dudinka.ru
+e-burg.ru
+grozny.ru
+irkutsk.ru
+ivanovo.ru
+izhevsk.ru
+jar.ru
+joshkar-ola.ru
+kalmykia.ru
+kaluga.ru
+kamchatka.ru
+karelia.ru
+kazan.ru
+kchr.ru
+kemerovo.ru
+khabarovsk.ru
+khakassia.ru
+khv.ru
+kirov.ru
+koenig.ru
+komi.ru
+kostroma.ru
+krasnoyarsk.ru
+kuban.ru
+kurgan.ru
+kursk.ru
+lipetsk.ru
+magadan.ru
+mari.ru
+mari-el.ru
+marine.ru
+mordovia.ru
+// mosreg.ru Bug 1090800 - removed at request of Aleksey Konstantinov <konstantinovav@mosreg.ru>
+msk.ru
+murmansk.ru
+nalchik.ru
+nnov.ru
+nov.ru
+novosibirsk.ru
+nsk.ru
+omsk.ru
+orenburg.ru
+oryol.ru
+palana.ru
+penza.ru
+perm.ru
+ptz.ru
+rnd.ru
+ryazan.ru
+sakhalin.ru
+samara.ru
+saratov.ru
+simbirsk.ru
+smolensk.ru
+spb.ru
+stavropol.ru
+stv.ru
+surgut.ru
+tambov.ru
+tatarstan.ru
+tom.ru
+tomsk.ru
+tsaritsyn.ru
+tsk.ru
+tula.ru
+tuva.ru
+tver.ru
+tyumen.ru
+udm.ru
+udmurtia.ru
+ulan-ude.ru
+vladikavkaz.ru
+vladimir.ru
+vladivostok.ru
+volgograd.ru
+vologda.ru
+voronezh.ru
+vrn.ru
+vyatka.ru
+yakutia.ru
+yamal.ru
+yaroslavl.ru
+yekaterinburg.ru
+yuzhno-sakhalinsk.ru
+// More geographical domains
+amursk.ru
+baikal.ru
+cmw.ru
+fareast.ru
+jamal.ru
+kms.ru
+k-uralsk.ru
+kustanai.ru
+kuzbass.ru
+magnitka.ru
+mytis.ru
+nakhodka.ru
+nkz.ru
+norilsk.ru
+oskol.ru
+pyatigorsk.ru
+rubtsovsk.ru
+snz.ru
+syzran.ru
+vdonsk.ru
+zgrad.ru
+// State domains
+gov.ru
+mil.ru
+// Technical domains
+test.ru
+
+// rw : http://www.nic.rw/cgi-bin/policy.pl
+rw
+gov.rw
+net.rw
+edu.rw
+ac.rw
+com.rw
+co.rw
+int.rw
+mil.rw
+gouv.rw
+
+// sa : http://www.nic.net.sa/
+sa
+com.sa
+net.sa
+org.sa
+gov.sa
+med.sa
+pub.sa
+edu.sa
+sch.sa
+
+// sb : http://www.sbnic.net.sb/
+// Submitted by registry <lee.humphries@telekom.com.sb> 2008-06-08
+sb
+com.sb
+edu.sb
+gov.sb
+net.sb
+org.sb
+
+// sc : http://www.nic.sc/
+sc
+com.sc
+gov.sc
+net.sc
+org.sc
+edu.sc
+
+// sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm
+// Submitted by registry <admin@isoc.sd> 2008-06-17
+sd
+com.sd
+net.sd
+org.sd
+edu.sd
+med.sd
+tv.sd
+gov.sd
+info.sd
+
+// se : http://en.wikipedia.org/wiki/.se
+// Submitted by registry <patrik.wallstrom@iis.se> 2014-03-18
+se
+a.se
+ac.se
+b.se
+bd.se
+brand.se
+c.se
+d.se
+e.se
+f.se
+fh.se
+fhsk.se
+fhv.se
+g.se
+h.se
+i.se
+k.se
+komforb.se
+kommunalforbund.se
+komvux.se
+l.se
+lanbib.se
+m.se
+n.se
+naturbruksgymn.se
+o.se
+org.se
+p.se
+parti.se
+pp.se
+press.se
+r.se
+s.se
+t.se
+tm.se
+u.se
+w.se
+x.se
+y.se
+z.se
+
+// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines
+sg
+com.sg
+net.sg
+org.sg
+gov.sg
+edu.sg
+per.sg
+
+// sh : http://www.nic.sh/registrar.html
+sh
+com.sh
+net.sh
+gov.sh
+org.sh
+mil.sh
+
+// si : http://en.wikipedia.org/wiki/.si
+si
+
+// sj : No registrations at this time.
+// Submitted by registry <jarle@uninett.no> 2008-06-16
+sj
+
+// sk : http://en.wikipedia.org/wiki/.sk
+// list of 2nd level domains ?
+sk
+
+// sl : http://www.nic.sl
+// Submitted by registry <adam@neoip.com> 2008-06-12
+sl
+com.sl
+net.sl
+edu.sl
+gov.sl
+org.sl
+
+// sm : http://en.wikipedia.org/wiki/.sm
+sm
+
+// sn : http://en.wikipedia.org/wiki/.sn
+sn
+art.sn
+com.sn
+edu.sn
+gouv.sn
+org.sn
+perso.sn
+univ.sn
+
+// so : http://www.soregistry.com/
+so
+com.so
+net.so
+org.so
+
+// sr : http://en.wikipedia.org/wiki/.sr
+sr
+
+// st : http://www.nic.st/html/policyrules/
+st
+co.st
+com.st
+consulado.st
+edu.st
+embaixada.st
+gov.st
+mil.st
+net.st
+org.st
+principe.st
+saotome.st
+store.st
+
+// su : http://en.wikipedia.org/wiki/.su
+su
+adygeya.su
+arkhangelsk.su
+balashov.su
+bashkiria.su
+bryansk.su
+dagestan.su
+grozny.su
+ivanovo.su
+kalmykia.su
+kaluga.su
+karelia.su
+khakassia.su
+krasnodar.su
+kurgan.su
+lenug.su
+mordovia.su
+msk.su
+murmansk.su
+nalchik.su
+nov.su
+obninsk.su
+penza.su
+pokrovsk.su
+sochi.su
+spb.su
+togliatti.su
+troitsk.su
+tula.su
+tuva.su
+vladikavkaz.su
+vladimir.su
+vologda.su
+
+// sv : http://www.svnet.org.sv/niveldos.pdf
+sv
+com.sv
+edu.sv
+gob.sv
+org.sv
+red.sv
+
+// sx : http://en.wikipedia.org/wiki/.sx
+// Confirmed by registry <jcvignes@openregistry.com> 2012-05-31
+sx
+gov.sx
+
+// sy : http://en.wikipedia.org/wiki/.sy
+// see also: http://www.gobin.info/domainname/sy.doc
+sy
+edu.sy
+gov.sy
+net.sy
+mil.sy
+com.sy
+org.sy
+
+// sz : http://en.wikipedia.org/wiki/.sz
+// http://www.sispa.org.sz/
+sz
+co.sz
+ac.sz
+org.sz
+
+// tc : http://en.wikipedia.org/wiki/.tc
+tc
+
+// td : http://en.wikipedia.org/wiki/.td
+td
+
+// tel: http://en.wikipedia.org/wiki/.tel
+// http://www.telnic.org/
+tel
+
+// tf : http://en.wikipedia.org/wiki/.tf
+tf
+
+// tg : http://en.wikipedia.org/wiki/.tg
+// http://www.nic.tg/
+tg
+
+// th : http://en.wikipedia.org/wiki/.th
+// Submitted by registry <krit@thains.co.th> 2008-06-17
+th
+ac.th
+co.th
+go.th
+in.th
+mi.th
+net.th
+or.th
+
+// tj : http://www.nic.tj/policy.html
+tj
+ac.tj
+biz.tj
+co.tj
+com.tj
+edu.tj
+go.tj
+gov.tj
+int.tj
+mil.tj
+name.tj
+net.tj
+nic.tj
+org.tj
+test.tj
+web.tj
+
+// tk : http://en.wikipedia.org/wiki/.tk
+tk
+
+// tl : http://en.wikipedia.org/wiki/.tl
+tl
+gov.tl
+
+// tm : http://www.nic.tm/local.html
+tm
+com.tm
+co.tm
+org.tm
+net.tm
+nom.tm
+gov.tm
+mil.tm
+edu.tm
+
+// tn : http://en.wikipedia.org/wiki/.tn
+// http://whois.ati.tn/
+tn
+com.tn
+ens.tn
+fin.tn
+gov.tn
+ind.tn
+intl.tn
+nat.tn
+net.tn
+org.tn
+info.tn
+perso.tn
+tourism.tn
+edunet.tn
+rnrt.tn
+rns.tn
+rnu.tn
+mincom.tn
+agrinet.tn
+defense.tn
+turen.tn
+
+// to : http://en.wikipedia.org/wiki/.to
+// Submitted by registry <egullich@colo.to> 2008-06-17
+to
+com.to
+gov.to
+net.to
+org.to
+edu.to
+mil.to
+
+// tp : No registrations at this time.
+// Submitted by Ryan Sleevi <ryan.sleevi@gmail.com> 2014-01-03
+tp
+
+// subTLDs: https://www.nic.tr/forms/eng/policies.pdf
+// and: https://www.nic.tr/forms/politikalar.pdf
+// Submitted by <mehmetgurevin@gmail.com> 2014-07-19
+tr
+com.tr
+info.tr
+biz.tr
+net.tr
+org.tr
+web.tr
+gen.tr
+tv.tr
+av.tr
+dr.tr
+bbs.tr
+name.tr
+tel.tr
+gov.tr
+bel.tr
+pol.tr
+mil.tr
+k12.tr
+edu.tr
+kep.tr
+
+// Used by Northern Cyprus
+nc.tr
+
+// Used by government agencies of Northern Cyprus
+gov.nc.tr
+
+// travel : http://en.wikipedia.org/wiki/.travel
+travel
+
+// tt : http://www.nic.tt/
+tt
+co.tt
+com.tt
+org.tt
+net.tt
+biz.tt
+info.tt
+pro.tt
+int.tt
+coop.tt
+jobs.tt
+mobi.tt
+travel.tt
+museum.tt
+aero.tt
+name.tt
+gov.tt
+edu.tt
+
+// tv : http://en.wikipedia.org/wiki/.tv
+// Not listing any 2LDs as reserved since none seem to exist in practice,
+// Wikipedia notwithstanding.
+tv
+
+// tw : http://en.wikipedia.org/wiki/.tw
+tw
+edu.tw
+gov.tw
+mil.tw
+com.tw
+net.tw
+org.tw
+idv.tw
+game.tw
+ebiz.tw
+club.tw
+網路.tw
+組織.tw
+商業.tw
+
+// tz : http://www.tznic.or.tz/index.php/domains
+// Confirmed by registry <manager@tznic.or.tz> 2013-01-22
+tz
+ac.tz
+co.tz
+go.tz
+hotel.tz
+info.tz
+me.tz
+mil.tz
+mobi.tz
+ne.tz
+or.tz
+sc.tz
+tv.tz
+
+// ua : https://hostmaster.ua/policy/?ua
+// Submitted by registry <dk@cctld.ua> 2012-04-27
+ua
+// ua 2LD
+com.ua
+edu.ua
+gov.ua
+in.ua
+net.ua
+org.ua
+// ua geographic names
+// https://hostmaster.ua/2ld/
+cherkassy.ua
+cherkasy.ua
+chernigov.ua
+chernihiv.ua
+chernivtsi.ua
+chernovtsy.ua
+ck.ua
+cn.ua
+cr.ua
+crimea.ua
+cv.ua
+dn.ua
+dnepropetrovsk.ua
+dnipropetrovsk.ua
+dominic.ua
+donetsk.ua
+dp.ua
+if.ua
+ivano-frankivsk.ua
+kh.ua
+kharkiv.ua
+kharkov.ua
+kherson.ua
+khmelnitskiy.ua
+khmelnytskyi.ua
+kiev.ua
+kirovograd.ua
+km.ua
+kr.ua
+krym.ua
+ks.ua
+kv.ua
+kyiv.ua
+lg.ua
+lt.ua
+lugansk.ua
+lutsk.ua
+lv.ua
+lviv.ua
+mk.ua
+mykolaiv.ua
+nikolaev.ua
+od.ua
+odesa.ua
+odessa.ua
+pl.ua
+poltava.ua
+rivne.ua
+rovno.ua
+rv.ua
+sb.ua
+sebastopol.ua
+sevastopol.ua
+sm.ua
+sumy.ua
+te.ua
+ternopil.ua
+uz.ua
+uzhgorod.ua
+vinnica.ua
+vinnytsia.ua
+vn.ua
+volyn.ua
+yalta.ua
+zaporizhzhe.ua
+zaporizhzhia.ua
+zhitomir.ua
+zhytomyr.ua
+zp.ua
+zt.ua
+
+// Private registries in .ua
+co.ua
+pp.ua
+
+// ug : https://www.registry.co.ug/
+ug
+co.ug
+or.ug
+ac.ug
+sc.ug
+go.ug
+ne.ug
+com.ug
+org.ug
+
+// uk : http://en.wikipedia.org/wiki/.uk
+// Submitted by registry <Michael.Daly@nominet.org.uk>
+uk
+ac.uk
+co.uk
+gov.uk
+ltd.uk
+me.uk
+net.uk
+nhs.uk
+org.uk
+plc.uk
+police.uk
+*.sch.uk
+
+// us : http://en.wikipedia.org/wiki/.us
+us
+dni.us
+fed.us
+isa.us
+kids.us
+nsn.us
+// us geographic names
+ak.us
+al.us
+ar.us
+as.us
+az.us
+ca.us
+co.us
+ct.us
+dc.us
+de.us
+fl.us
+ga.us
+gu.us
+hi.us
+ia.us
+id.us
+il.us
+in.us
+ks.us
+ky.us
+la.us
+ma.us
+md.us
+me.us
+mi.us
+mn.us
+mo.us
+ms.us
+mt.us
+nc.us
+nd.us
+ne.us
+nh.us
+nj.us
+nm.us
+nv.us
+ny.us
+oh.us
+ok.us
+or.us
+pa.us
+pr.us
+ri.us
+sc.us
+sd.us
+tn.us
+tx.us
+ut.us
+vi.us
+vt.us
+va.us
+wa.us
+wi.us
+wv.us
+wy.us
+// The registrar notes several more specific domains available in each state,
+// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat
+// haphazard; in some states these domains resolve as addresses, while in others
+// only subdomains are available, or even nothing at all. We include the
+// most common ones where it's clear that different sites are different
+// entities.
+k12.ak.us
+k12.al.us
+k12.ar.us
+k12.as.us
+k12.az.us
+k12.ca.us
+k12.co.us
+k12.ct.us
+k12.dc.us
+k12.de.us
+k12.fl.us
+k12.ga.us
+k12.gu.us
+// k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login
+k12.ia.us
+k12.id.us
+k12.il.us
+k12.in.us
+k12.ks.us
+k12.ky.us
+k12.la.us
+k12.ma.us
+k12.md.us
+k12.me.us
+k12.mi.us
+k12.mn.us
+k12.mo.us
+k12.ms.us
+k12.mt.us
+k12.nc.us
+// k12.nd.us Bug 1028347 - Removed at request of Travis Rosso <trossow@nd.gov>
+k12.ne.us
+k12.nh.us
+k12.nj.us
+k12.nm.us
+k12.nv.us
+k12.ny.us
+k12.oh.us
+k12.ok.us
+k12.or.us
+k12.pa.us
+k12.pr.us
+k12.ri.us
+k12.sc.us
+// k12.sd.us Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us>
+k12.tn.us
+k12.tx.us
+k12.ut.us
+k12.vi.us
+k12.vt.us
+k12.va.us
+k12.wa.us
+k12.wi.us
+// k12.wv.us Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu>
+k12.wy.us
+cc.ak.us
+cc.al.us
+cc.ar.us
+cc.as.us
+cc.az.us
+cc.ca.us
+cc.co.us
+cc.ct.us
+cc.dc.us
+cc.de.us
+cc.fl.us
+cc.ga.us
+cc.gu.us
+cc.hi.us
+cc.ia.us
+cc.id.us
+cc.il.us
+cc.in.us
+cc.ks.us
+cc.ky.us
+cc.la.us
+cc.ma.us
+cc.md.us
+cc.me.us
+cc.mi.us
+cc.mn.us
+cc.mo.us
+cc.ms.us
+cc.mt.us
+cc.nc.us
+cc.nd.us
+cc.ne.us
+cc.nh.us
+cc.nj.us
+cc.nm.us
+cc.nv.us
+cc.ny.us
+cc.oh.us
+cc.ok.us
+cc.or.us
+cc.pa.us
+cc.pr.us
+cc.ri.us
+cc.sc.us
+cc.sd.us
+cc.tn.us
+cc.tx.us
+cc.ut.us
+cc.vi.us
+cc.vt.us
+cc.va.us
+cc.wa.us
+cc.wi.us
+cc.wv.us
+cc.wy.us
+lib.ak.us
+lib.al.us
+lib.ar.us
+lib.as.us
+lib.az.us
+lib.ca.us
+lib.co.us
+lib.ct.us
+lib.dc.us
+lib.de.us
+lib.fl.us
+lib.ga.us
+lib.gu.us
+lib.hi.us
+lib.ia.us
+lib.id.us
+lib.il.us
+lib.in.us
+lib.ks.us
+lib.ky.us
+lib.la.us
+lib.ma.us
+lib.md.us
+lib.me.us
+lib.mi.us
+lib.mn.us
+lib.mo.us
+lib.ms.us
+lib.mt.us
+lib.nc.us
+lib.nd.us
+lib.ne.us
+lib.nh.us
+lib.nj.us
+lib.nm.us
+lib.nv.us
+lib.ny.us
+lib.oh.us
+lib.ok.us
+lib.or.us
+lib.pa.us
+lib.pr.us
+lib.ri.us
+lib.sc.us
+lib.sd.us
+lib.tn.us
+lib.tx.us
+lib.ut.us
+lib.vi.us
+lib.vt.us
+lib.va.us
+lib.wa.us
+lib.wi.us
+// lib.wv.us Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us>
+lib.wy.us
+// k12.ma.us contains school districts in Massachusetts. The 4LDs are
+// managed indepedently except for private (PVT), charter (CHTR) and
+// parochial (PAROCH) schools. Those are delegated dorectly to the
+// 5LD operators. <k12-ma-hostmaster _ at _ rsuc.gweep.net>
+pvt.k12.ma.us
+chtr.k12.ma.us
+paroch.k12.ma.us
+
+// uy : http://www.nic.org.uy/
+uy
+com.uy
+edu.uy
+gub.uy
+mil.uy
+net.uy
+org.uy
+
+// uz : http://www.reg.uz/
+uz
+co.uz
+com.uz
+net.uz
+org.uz
+
+// va : http://en.wikipedia.org/wiki/.va
+va
+
+// vc : http://en.wikipedia.org/wiki/.vc
+// Submitted by registry <kshah@ca.afilias.info> 2008-06-13
+vc
+com.vc
+net.vc
+org.vc
+gov.vc
+mil.vc
+edu.vc
+
+// ve : https://registro.nic.ve/
+// Confirmed by registry 2012-10-04
+// Updated 2014-05-20 - Bug 940478
+ve
+arts.ve
+co.ve
+com.ve
+e12.ve
+edu.ve
+firm.ve
+gob.ve
+gov.ve
+info.ve
+int.ve
+mil.ve
+net.ve
+org.ve
+rec.ve
+store.ve
+tec.ve
+web.ve
+
+// vg : http://en.wikipedia.org/wiki/.vg
+vg
+
+// vi : http://www.nic.vi/newdomainform.htm
+// http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other
+// TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they
+// are available for registration (which they do not seem to be).
+vi
+co.vi
+com.vi
+k12.vi
+net.vi
+org.vi
+
+// vn : https://www.dot.vn/vnnic/vnnic/domainregistration.jsp
+vn
+com.vn
+net.vn
+org.vn
+edu.vn
+gov.vn
+int.vn
+ac.vn
+biz.vn
+info.vn
+name.vn
+pro.vn
+health.vn
+
+// vu : http://en.wikipedia.org/wiki/.vu
+// http://www.vunic.vu/
+vu
+com.vu
+edu.vu
+net.vu
+org.vu
+
+// wf : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf
+wf
+
+// ws : http://en.wikipedia.org/wiki/.ws
+// http://samoanic.ws/index.dhtml
+ws
+com.ws
+net.ws
+org.ws
+gov.ws
+edu.ws
+
+// yt : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf
+yt
+
+// IDN ccTLDs
+// Please sort by ISO 3166 ccTLD, then punicode string
+// when submitting patches and follow this format:
+// <Punicode> ("<english word>" <language>) : <ISO 3166 ccTLD>
+// [optional sponsoring org]
+// <URL>
+
+// xn--mgbaam7a8h ("Emerat" Arabic) : AE
+// http://nic.ae/english/arabicdomain/rules.jsp
+امارات
+
+// xn--54b7fta0cc ("Bangla" Bangla) : BD
+বাংলা
+
+// xn--fiqs8s ("China" Chinese-Han-Simplified <.Zhongguo>) : CN
+// CNNIC
+// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
+中国
+
+// xn--fiqz9s ("China" Chinese-Han-Traditional <.Zhongguo>) : CN
+// CNNIC
+// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
+中國
+
+// xn--lgbbat1ad8j ("Algeria / Al Jazair" Arabic) : DZ
+الجزائر
+
+// xn--wgbh1c ("Egypt" Arabic .masr) : EG
+// http://www.dotmasr.eg/
+مصر
+
+// xn--node ("ge" Georgian (Mkhedruli)) : GE
+გე
+
+// xn--j6w193g ("Hong Kong" Chinese-Han) : HK
+// https://www2.hkirc.hk/register/rules.jsp
+香港
+
+// xn--h2brj9c ("Bharat" Devanagari) : IN
+// India
+भारत
+
+// xn--mgbbh1a71e ("Bharat" Arabic) : IN
+// India
+بھارت
+
+// xn--fpcrj9c3d ("Bharat" Telugu) : IN
+// India
+భారత్
+
+// xn--gecrj9c ("Bharat" Gujarati) : IN
+// India
+ભારત
+
+// xn--s9brj9c ("Bharat" Gurmukhi) : IN
+// India
+ਭਾਰਤ
+
+// xn--45brj9c ("Bharat" Bengali) : IN
+// India
+ভারত
+
+// xn--xkc2dl3a5ee0h ("India" Tamil) : IN
+// India
+இந்தியா
+
+// xn--mgba3a4f16a ("Iran" Persian) : IR
+ایران
+
+// xn--mgba3a4fra ("Iran" Arabic) : IR
+ايران
+
+// xn--mgbayh7gpa ("al-Ordon" Arabic) : JO
+// National Information Technology Center (NITC)
+// Royal Scientific Society, Al-Jubeiha
+الاردن
+
+// xn--3e0b707e ("Republic of Korea" Hangul) : KR
+한국
+
+// xn--80ao21a ("Kaz" Kazakh) : KZ
+қаз
+
+// xn--fzc2c9e2c ("Lanka" Sinhalese-Sinhala) : LK
+// http://nic.lk
+ලංකා
+
+// xn--xkc2al3hye2a ("Ilangai" Tamil) : LK
+// http://nic.lk
+இலங்கை
+
+// xn--mgbc0a9azcg ("Morocco / al-Maghrib" Arabic) : MA
+المغرب
+
+// xn--l1acc ("mon" Mongolian) : MN
+мон
+
+// xn--mgbx4cd0ab ("Malaysia" Malay) : MY
+مليسيا
+
+// xn--mgb9awbf ("Oman" Arabic) : OM
+عمان
+
+// xn--ygbi2ammx ("Falasteen" Arabic) : PS
+// The Palestinian National Internet Naming Authority (PNINA)
+// http://www.pnina.ps
+فلسطين
+
+// xn--90a3ac ("srb" Cyrillic) : RS
+// http://www.rnids.rs/en/the-.срб-domain
+срб
+пр.срб
+орг.срб
+обр.срб
+од.срб
+упр.срб
+ак.срб
+
+// xn--p1ai ("rf" Russian-Cyrillic) : RU
+// http://www.cctld.ru/en/docs/rulesrf.php
+рф
+
+// xn--wgbl6a ("Qatar" Arabic) : QA
+// http://www.ict.gov.qa/
+قطر
+
+// xn--mgberp4a5d4ar ("AlSaudiah" Arabic) : SA
+// http://www.nic.net.sa/
+السعودية
+
+// xn--mgberp4a5d4a87g ("AlSaudiah" Arabic) variant : SA
+السعودیة
+
+// xn--mgbqly7c0a67fbc ("AlSaudiah" Arabic) variant : SA
+السعودیۃ
+
+// xn--mgbqly7cvafr ("AlSaudiah" Arabic) variant : SA
+السعوديه
+
+// xn--ogbpf8fl ("Syria" Arabic) : SY
+سورية
+
+// xn--mgbtf8fl ("Syria" Arabic) variant : SY
+سوريا
+
+// xn--yfro4i67o Singapore ("Singapore" Chinese-Han) : SG
+新加坡
+
+// xn--clchc0ea0b2g2a9gcd ("Singapore" Tamil) : SG
+சிங்கப்பூர்
+
+// xn--o3cw4h ("Thai" Thai) : TH
+// http://www.thnic.co.th
+ไทย
+
+// xn--pgbs0dh ("Tunis") : TN
+// http://nic.tn
+تونس
+
+// xn--kpry57d ("Taiwan" Chinese-Han-Traditional) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台灣
+
+// xn--kprw13d ("Taiwan" Chinese-Han-Simplified) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台湾
+
+// xn--nnx388a ("Taiwan") variant : TW
+臺灣
+
+// xn--j1amh ("ukr" Cyrillic) : UA
+укр
+
+// xn--mgb2ddes ("AlYemen" Arabic) : YE
+اليمن
+
+// xxx : http://icmregistry.com
+xxx
+
+// ye : http://www.y.net.ye/services/domain_name.htm
+*.ye
+
+// za : http://www.zadna.org.za/slds.html
+*.za
+
+// zm : http://en.wikipedia.org/wiki/.zm
+*.zm
+
+// zw : http://en.wikipedia.org/wiki/.zw
+*.zw
+
+
+// List of new gTLDs imported from https://newgtlds.icann.org/newgtlds.csv on 2015-04-07T06:02:08Z
+
+// aaa : 2015-02-26 American Automobile Association, Inc.
+aaa
+
+// abb : 2014-10-24 ABB Ltd
+abb
+
+// abbott : 2014-07-24 Abbott Laboratories, Inc.
+abbott
+
+// abogado : 2014-04-24 Top Level Domain Holdings Limited
+abogado
+
+// academy : 2013-11-07 Half Oaks, LLC
+academy
+
+// accenture : 2014-08-15 Accenture plc
+accenture
+
+// accountant : 2014-11-20 dot Accountant Limited
+accountant
+
+// accountants : 2014-03-20 Knob Town, LLC
+accountants
+
+// aco : 2015-01-08 ACO Severin Ahlmann GmbH & Co. KG
+aco
+
+// active : 2014-05-01 The Active Network, Inc
+active
+
+// actor : 2013-12-12 United TLD Holdco Ltd.
+actor
+
+// ads : 2014-12-04 Charleston Road Registry Inc.
+ads
+
+// adult : 2014-10-16 ICM Registry AD LLC
+adult
+
+// aeg : 2015-03-19 Aktiebolaget Electrolux
+aeg
+
+// afl : 2014-10-02 Australian Football League
+afl
+
+// africa : 2014-03-24 ZA Central Registry NPC trading as Registry.Africa
+africa
+
+// africamagic : 2015-03-05 Electronic Media Network (Pty) Ltd
+africamagic
+
+// agency : 2013-11-14 Steel Falls, LLC
+agency
+
+// aig : 2014-12-18 American International Group, Inc.
+aig
+
+// airforce : 2014-03-06 United TLD Holdco Ltd.
+airforce
+
+// airtel : 2014-10-24 Bharti Airtel Limited
+airtel
+
+// alibaba : 2015-01-15 Alibaba Group Holding Limited
+alibaba
+
+// alipay : 2015-01-15 Alibaba Group Holding Limited
+alipay
+
+// allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
+allfinanz
+
+// alsace : 2014-07-02 REGION D ALSACE
+alsace
+
+// amsterdam : 2014-07-24 Gemeente Amsterdam
+amsterdam
+
+// analytics : 2014-12-18 Campus IP LLC
+analytics
+
+// android : 2014-08-07 Charleston Road Registry Inc.
+android
+
+// anquan : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+anquan
+
+// apartments : 2014-12-11 June Maple, LLC
+apartments
+
+// aquarelle : 2014-07-24 Aquarelle.com
+aquarelle
+
+// aramco : 2014-11-20 Aramco Services Company
+aramco
+
+// archi : 2014-02-06 STARTING DOT LIMITED
+archi
+
+// army : 2014-03-06 United TLD Holdco Ltd.
+army
+
+// arte : 2014-12-11 Association Relative à la Télévision Européenne G.E.I.E.
+arte
+
+// associates : 2014-03-06 Baxter Hill, LLC
+associates
+
+// attorney : 2014-03-20
+attorney
+
+// auction : 2014-03-20
+auction
+
+// audio : 2014-03-20 Uniregistry, Corp.
+audio
+
+// author : 2014-12-18 Amazon EU S.à r.l.
+author
+
+// auto : 2014-11-13 Uniregistry, Corp.
+auto
+
+// autos : 2014-01-09 DERAutos, LLC
+autos
+
+// avianca : 2015-01-08 Aerovias del Continente Americano S.A. Avianca
+avianca
+
+// axa : 2013-12-19 AXA SA
+axa
+
+// azure : 2014-12-18 Microsoft Corporation
+azure
+
+// baidu : 2015-01-08 Baidu, Inc.
+baidu
+
+// band : 2014-06-12
+band
+
+// bank : 2014-09-25 fTLD Registry Services LLC
+bank
+
+// bar : 2013-12-12 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+bar
+
+// barcelona : 2014-07-24 Municipi de Barcelona
+barcelona
+
+// barclaycard : 2014-11-20 Barclays Bank PLC
+barclaycard
+
+// barclays : 2014-11-20 Barclays Bank PLC
+barclays
+
+// bargains : 2013-11-14 Half Hallow, LLC
+bargains
+
+// bauhaus : 2014-04-17 Werkhaus GmbH
+bauhaus
+
+// bayern : 2014-01-23 Bayern Connect GmbH
+bayern
+
+// bbc : 2014-12-18 British Broadcasting Corporation
+bbc
+
+// bbva : 2014-10-02 BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
+bbva
+
+// bcg : 2015-04-02 The Boston Consulting Group, Inc.
+bcg
+
+// bcn : 2014-07-24 Municipi de Barcelona
+bcn
+
+// beer : 2014-01-09 Top Level Domain Holdings Limited
+beer
+
+// bentley : 2014-12-18 Bentley Motors Limited
+bentley
+
+// berlin : 2013-10-31 dotBERLIN GmbH & Co. KG
+berlin
+
+// best : 2013-12-19 BestTLD Pty Ltd
+best
+
+// bharti : 2014-01-09 Bharti Enterprises (Holding) Private Limited
+bharti
+
+// bible : 2014-06-19 American Bible Society
+bible
+
+// bid : 2013-12-19 dot Bid Limited
+bid
+
+// bike : 2013-08-27 Grand Hollow, LLC
+bike
+
+// bing : 2014-12-18 Microsoft Corporation
+bing
+
+// bingo : 2014-12-04 Sand Cedar, LLC
+bingo
+
+// bio : 2014-03-06 STARTING DOT LIMITED
+bio
+
+// black : 2014-01-16 Afilias Limited
+black
+
+// blackfriday : 2014-01-16 Uniregistry, Corp.
+blackfriday
+
+// bloomberg : 2014-07-17 Bloomberg IP Holdings LLC
+bloomberg
+
+// blue : 2013-11-07 Afilias Limited
+blue
+
+// bms : 2014-10-30 Bristol-Myers Squibb Company
+bms
+
+// bmw : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
+bmw
+
+// bnl : 2014-07-24 Banca Nazionale del Lavoro
+bnl
+
+// bnpparibas : 2014-05-29 BNP Paribas
+bnpparibas
+
+// boats : 2014-12-04 DERBoats, LLC
+boats
+
+// bom : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+bom
+
+// bond : 2014-06-05 Bond University Limited
+bond
+
+// boo : 2014-01-30 Charleston Road Registry Inc.
+boo
+
+// boots : 2015-01-08 THE BOOTS COMPANY PLC
+boots
+
+// bot : 2014-12-18 Amazon EU S.à r.l.
+bot
+
+// boutique : 2013-11-14 Over Galley, LLC
+boutique
+
+// bradesco : 2014-12-18 Banco Bradesco S.A.
+bradesco
+
+// bridgestone : 2014-12-18 Bridgestone Corporation
+bridgestone
+
+// broadway : 2014-12-22 Celebrate Broadway, Inc.
+broadway
+
+// broker : 2014-12-11 IG Group Holdings PLC
+broker
+
+// brother : 2015-01-29 Brother Industries, Ltd.
+brother
+
+// brussels : 2014-02-06 DNS.be vzw
+brussels
+
+// budapest : 2013-11-21 Top Level Domain Holdings Limited
+budapest
+
+// build : 2013-11-07 Plan Bee LLC
+build
+
+// builders : 2013-11-07 Atomic Madison, LLC
+builders
+
+// business : 2013-11-07 Spring Cross, LLC
+business
+
+// buy : 2014-12-18 Amazon EU S.à r.l.
+buy
+
+// buzz : 2013-10-02 DOTSTRATEGY CO.
+buzz
+
+// bzh : 2014-02-27 Association www.bzh
+bzh
+
+// cab : 2013-10-24 Half Sunset, LLC
+cab
+
+// cafe : 2015-02-11 Pioneer Canyon, LLC
+cafe
+
+// cal : 2014-07-24 Charleston Road Registry Inc.
+cal
+
+// call : 2014-12-18 Amazon EU S.à r.l.
+call
+
+// camera : 2013-08-27 Atomic Maple, LLC
+camera
+
+// camp : 2013-11-07 Delta Dynamite, LLC
+camp
+
+// cancerresearch : 2014-05-15 Australian Cancer Research Foundation
+cancerresearch
+
+// canon : 2014-09-12 Canon Inc.
+canon
+
+// capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+capetown
+
+// capital : 2014-03-06 Delta Mill, LLC
+capital
+
+// car : 2015-01-22 Charleston Road Registry Inc.
+car
+
+// caravan : 2013-12-12 Caravan International, Inc.
+caravan
+
+// cards : 2013-12-05 Foggy Hollow, LLC
+cards
+
+// care : 2014-03-06 Goose Cross
+care
+
+// career : 2013-10-09 dotCareer LLC
+career
+
+// careers : 2013-10-02 Wild Corner, LLC
+careers
+
+// cars : 2014-11-13 Uniregistry, Corp.
+cars
+
+// cartier : 2014-06-23 Richemont DNS Inc.
+cartier
+
+// casa : 2013-11-21 Top Level Domain Holdings Limited
+casa
+
+// cash : 2014-03-06 Delta Lake, LLC
+cash
+
+// casino : 2014-12-18 Binky Sky, LLC
+casino
+
+// catering : 2013-12-05 New Falls. LLC
+catering
+
+// cba : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+cba
+
+// cbn : 2014-08-22 The Christian Broadcasting Network, Inc.
+cbn
+
+// center : 2013-11-07 Tin Mill, LLC
+center
+
+// ceo : 2013-11-07 CEOTLD Pty Ltd
+ceo
+
+// cern : 2014-06-05 European Organization for Nuclear Research (\
+cern
+
+// cfa : 2014-08-28 CFA Institute
+cfa
+
+// cfd : 2014-12-11 IG Group Holdings PLC
+cfd
+
+// channel : 2014-05-08 Charleston Road Registry Inc.
+channel
+
+// chat : 2014-12-04 Sand Fields, LLC
+chat
+
+// cheap : 2013-11-14 Sand Cover, LLC
+cheap
+
+// chloe : 2014-10-16 Richemont DNS Inc.
+chloe
+
+// christmas : 2013-11-21 Uniregistry, Corp.
+christmas
+
+// chrome : 2014-07-24 Charleston Road Registry Inc.
+chrome
+
+// church : 2014-02-06 Holly Fields, LLC
+church
+
+// cipriani : 2015-02-19 Hotel Cipriani Srl
+cipriani
+
+// circle : 2014-12-18 Amazon EU S.à r.l.
+circle
+
+// cisco : 2014-12-22 Cisco Technology, Inc.
+cisco
+
+// citic : 2014-01-09 CITIC Group Corporation
+citic
+
+// city : 2014-05-29 Snow Sky, LLC
+city
+
+// cityeats : 2014-12-11 Lifestyle Domain Holdings, Inc.
+cityeats
+
+// claims : 2014-03-20 Black Corner, LLC
+claims
+
+// cleaning : 2013-12-05 Fox Shadow, LLC
+cleaning
+
+// click : 2014-06-05 Uniregistry, Corp.
+click
+
+// clinic : 2014-03-20 Goose Park, LLC
+clinic
+
+// clothing : 2013-08-27 Steel Lake, LLC
+clothing
+
+// club : 2013-11-08 .CLUB DOMAINS, LLC
+club
+
+// coach : 2014-10-09 Koko Island, LLC
+coach
+
+// codes : 2013-10-31 Puff Willow, LLC
+codes
+
+// coffee : 2013-10-17 Trixy Cover, LLC
+coffee
+
+// college : 2014-01-16 XYZ.COM LLC
+college
+
+// cologne : 2014-02-05 NetCologne Gesellschaft für Telekommunikation mbH
+cologne
+
+// commbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+commbank
+
+// community : 2013-12-05 Fox Orchard, LLC
+community
+
+// company : 2013-11-07 Silver Avenue, LLC
+company
+
+// computer : 2013-10-24 Pine Mill, LLC
+computer
+
+// comsec : 2015-01-08 VeriSign, Inc.
+comsec
+
+// condos : 2013-12-05 Pine House, LLC
+condos
+
+// construction : 2013-09-16 Fox Dynamite, LLC
+construction
+
+// consulting : 2013-12-05
+consulting
+
+// contact : 2015-01-08 Top Level Spectrum, Inc.
+contact
+
+// contractors : 2013-09-10 Magic Woods, LLC
+contractors
+
+// cooking : 2013-11-21 Top Level Domain Holdings Limited
+cooking
+
+// cool : 2013-11-14 Koko Lake, LLC
+cool
+
+// corsica : 2014-09-25 Collectivité Territoriale de Corse
+corsica
+
+// country : 2013-12-19 Top Level Domain Holdings Limited
+country
+
+// coupon : 2015-02-26 Amazon EU S.à r.l.
+coupon
+
+// coupons : 2015-03-26 Black Island, LLC
+coupons
+
+// courses : 2014-12-04 OPEN UNIVERSITIES AUSTRALIA PTY LTD
+courses
+
+// credit : 2014-03-20 Snow Shadow, LLC
+credit
+
+// creditcard : 2014-03-20 Binky Frostbite, LLC
+creditcard
+
+// creditunion : 2015-01-22 CUNA Performance Resources, LLC
+creditunion
+
+// cricket : 2014-10-09 dot Cricket Limited
+cricket
+
+// crown : 2014-10-24 Crown Equipment Corporation
+crown
+
+// crs : 2014-04-03 Federated Co-operatives Limited
+crs
+
+// cruises : 2013-12-05 Spring Way, LLC
+cruises
+
+// csc : 2014-09-25 Alliance-One Services, Inc.
+csc
+
+// cuisinella : 2014-04-03 SALM S.A.S.
+cuisinella
+
+// cymru : 2014-05-08 Nominet UK
+cymru
+
+// cyou : 2015-01-22 Beijing Gamease Age Digital Technology Co., Ltd.
+cyou
+
+// dabur : 2014-02-06 Dabur India Limited
+dabur
+
+// dad : 2014-01-23 Charleston Road Registry Inc.
+dad
+
+// dance : 2013-10-24 United TLD Holdco Ltd.
+dance
+
+// date : 2014-11-20 dot Date Limited
+date
+
+// dating : 2013-12-05 Pine Fest, LLC
+dating
+
+// datsun : 2014-03-27 NISSAN MOTOR CO., LTD.
+datsun
+
+// day : 2014-01-30 Charleston Road Registry Inc.
+day
+
+// dclk : 2014-11-20 Charleston Road Registry Inc.
+dclk
+
+// dealer : 2014-12-22 Dealer Dot Com, Inc.
+dealer
+
+// deals : 2014-05-22 Sand Sunset, LLC
+deals
+
+// degree : 2014-03-06
+degree
+
+// delivery : 2014-09-11 Steel Station, LLC
+delivery
+
+// dell : 2014-10-24 Dell Inc.
+dell
+
+// delta : 2015-02-19 Delta Air Lines, Inc.
+delta
+
+// democrat : 2013-10-24 United TLD Holdco Ltd.
+democrat
+
+// dental : 2014-03-20 Tin Birch, LLC
+dental
+
+// dentist : 2014-03-20
+dentist
+
+// desi : 2013-11-14 Desi Networks LLC
+desi
+
+// design : 2014-11-07 Top Level Design, LLC
+design
+
+// dev : 2014-10-16 Charleston Road Registry Inc.
+dev
+
+// diamonds : 2013-09-22 John Edge, LLC
+diamonds
+
+// diet : 2014-06-26 Uniregistry, Corp.
+diet
+
+// digital : 2014-03-06 Dash Park, LLC
+digital
+
+// direct : 2014-04-10 Half Trail, LLC
+direct
+
+// directory : 2013-09-20 Extra Madison, LLC
+directory
+
+// discount : 2014-03-06 Holly Hill, LLC
+discount
+
+// dnp : 2013-12-13 Dai Nippon Printing Co., Ltd.
+dnp
+
+// docs : 2014-10-16 Charleston Road Registry Inc.
+docs
+
+// dog : 2014-12-04 Koko Mill, LLC
+dog
+
+// doha : 2014-09-18 Communications Regulatory Authority (CRA)
+doha
+
+// domains : 2013-10-17 Sugar Cross, LLC
+domains
+
+// doosan : 2014-04-03 Doosan Corporation
+doosan
+
+// download : 2014-11-20 dot Support Limited
+download
+
+// drive : 2015-03-05 Charleston Road Registry Inc.
+drive
+
+// dstv : 2015-03-12 MultiChoice (Proprietary) Limited
+dstv
+
+// dubai : 2015-01-01 Dubai Smart Government Department
+dubai
+
+// durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+durban
+
+// dvag : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+dvag
+
+// earth : 2014-12-04 Interlink Co., Ltd.
+earth
+
+// eat : 2014-01-23 Charleston Road Registry Inc.
+eat
+
+// edeka : 2014-12-18 EDEKA Verband kaufmännischer Genossenschaften e.V.
+edeka
+
+// education : 2013-11-07 Brice Way, LLC
+education
+
+// email : 2013-10-31 Spring Madison, LLC
+email
+
+// emerck : 2014-04-03 Merck KGaA
+emerck
+
+// energy : 2014-09-11 Binky Birch, LLC
+energy
+
+// engineer : 2014-03-06 United TLD Holdco Ltd.
+engineer
+
+// engineering : 2014-03-06 Romeo Canyon
+engineering
+
+// enterprises : 2013-09-20 Snow Oaks, LLC
+enterprises
+
+// epson : 2014-12-04 Seiko Epson Corporation
+epson
+
+// equipment : 2013-08-27 Corn Station, LLC
+equipment
+
+// erni : 2014-04-03 ERNI Group Holding AG
+erni
+
+// esq : 2014-05-08 Charleston Road Registry Inc.
+esq
+
+// estate : 2013-08-27 Trixy Park, LLC
+estate
+
+// eurovision : 2014-04-24 European Broadcasting Union (EBU)
+eurovision
+
+// eus : 2013-12-12 Puntueus Fundazioa
+eus
+
+// events : 2013-12-05 Pioneer Maple, LLC
+events
+
+// everbank : 2014-05-15 EverBank
+everbank
+
+// exchange : 2014-03-06 Spring Falls, LLC
+exchange
+
+// expert : 2013-11-21 Magic Pass, LLC
+expert
+
+// exposed : 2013-12-05 Victor Beach, LLC
+exposed
+
+// express : 2015-02-11 Sea Sunset, LLC
+express
+
+// fage : 2014-12-18 Fage International S.A.
+fage
+
+// fail : 2014-03-06 Atomic Pipe, LLC
+fail
+
+// fairwinds : 2014-11-13 FairWinds Partners, LLC
+fairwinds
+
+// faith : 2014-11-20 dot Faith Limited
+faith
+
+// family : 2015-04-02 Bitter Galley, LLC
+family
+
+// fan : 2014-03-06
+fan
+
+// fans : 2014-11-07 Asiamix Digital Limited
+fans
+
+// farm : 2013-11-07 Just Maple, LLC
+farm
+
+// fashion : 2014-07-03 Top Level Domain Holdings Limited
+fashion
+
+// fast : 2014-12-18 Amazon EU S.à r.l.
+fast
+
+// feedback : 2013-12-19 Top Level Spectrum, Inc.
+feedback
+
+// ferrero : 2014-12-18 Ferrero Trading Lux S.A.
+ferrero
+
+// film : 2015-01-08 Motion Picture Domain Registry Pty Ltd
+film
+
+// final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+final
+
+// finance : 2014-03-20 Cotton Cypress, LLC
+finance
+
+// financial : 2014-03-06 Just Cover, LLC
+financial
+
+// firestone : 2014-12-18 Bridgestone Corporation
+firestone
+
+// firmdale : 2014-03-27 Firmdale Holdings Limited
+firmdale
+
+// fish : 2013-12-12 Fox Woods, LLC
+fish
+
+// fishing : 2013-11-21 Top Level Domain Holdings Limited
+fishing
+
+// fit : 2014-11-07 Top Level Domain Holdings Limited
+fit
+
+// fitness : 2014-03-06 Brice Orchard, LLC
+fitness
+
+// flickr : 2015-04-02 Yahoo! Domain Services Inc.
+flickr
+
+// flights : 2013-12-05 Fox Station, LLC
+flights
+
+// florist : 2013-11-07 Half Cypress, LLC
+florist
+
+// flowers : 2014-10-09 Uniregistry, Corp.
+flowers
+
+// flsmidth : 2014-07-24 FLSmidth A/S
+flsmidth
+
+// fly : 2014-05-08 Charleston Road Registry Inc.
+fly
+
+// foo : 2014-01-23 Charleston Road Registry Inc.
+foo
+
+// football : 2014-12-18 Foggy Farms, LLC
+football
+
+// ford : 2014-11-13 Ford Motor Company
+ford
+
+// forex : 2014-12-11 IG Group Holdings PLC
+forex
+
+// forsale : 2014-05-22
+forsale
+
+// forum : 2015-04-02 Fegistry, LLC
+forum
+
+// foundation : 2013-12-05 John Dale, LLC
+foundation
+
+// frl : 2014-05-15 FRLregistry B.V.
+frl
+
+// frogans : 2013-12-19 OP3FT
+frogans
+
+// frontier : 2015-02-05 Frontier Communications Corporation
+frontier
+
+// fund : 2014-03-20 John Castle, LLC
+fund
+
+// furniture : 2014-03-20 Lone Fields, LLC
+furniture
+
+// futbol : 2013-09-20
+futbol
+
+// fyi : 2015-04-02 Silver Tigers, LLC
+fyi
+
+// gal : 2013-11-07 Asociación puntoGAL
+gal
+
+// gallery : 2013-09-13 Sugar House, LLC
+gallery
+
+// gallup : 2015-02-19 Gallup, Inc.
+gallup
+
+// garden : 2014-06-26 Top Level Domain Holdings Limited
+garden
+
+// gbiz : 2014-07-17 Charleston Road Registry Inc.
+gbiz
+
+// gdn : 2014-07-31 Joint Stock Company \
+gdn
+
+// gea : 2014-12-04 GEA Group Aktiengesellschaft
+gea
+
+// gent : 2014-01-23 COMBELL GROUP NV/SA
+gent
+
+// genting : 2015-03-12 Resorts World Inc Pte. Ltd.
+genting
+
+// ggee : 2014-01-09 GMO Internet, Inc.
+ggee
+
+// gift : 2013-10-17 Uniregistry, Corp.
+gift
+
+// gifts : 2014-07-03 Goose Sky, LLC
+gifts
+
+// gives : 2014-03-06 United TLD Holdco Ltd.
+gives
+
+// giving : 2014-11-13 Giving Limited
+giving
+
+// glass : 2013-11-07 Black Cover, LLC
+glass
+
+// gle : 2014-07-24 Charleston Road Registry Inc.
+gle
+
+// global : 2014-04-17 Dot GLOBAL AS
+global
+
+// globo : 2013-12-19 Globo Comunicação e Participações S.A
+globo
+
+// gmail : 2014-05-01 Charleston Road Registry Inc.
+gmail
+
+// gmo : 2014-01-09 GMO Internet, Inc.
+gmo
+
+// gmx : 2014-04-24 1&1 Mail & Media GmbH
+gmx
+
+// gold : 2015-01-22 June Edge, LLC
+gold
+
+// goldpoint : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
+goldpoint
+
+// golf : 2014-12-18 Lone falls, LLC
+golf
+
+// goo : 2014-12-18 NTT Resonant Inc.
+goo
+
+// goog : 2014-11-20 Charleston Road Registry Inc.
+goog
+
+// google : 2014-07-24 Charleston Road Registry Inc.
+google
+
+// gop : 2014-01-16 Republican State Leadership Committee, Inc.
+gop
+
+// got : 2014-12-18 Amazon EU S.à r.l.
+got
+
+// gotv : 2015-03-12 MultiChoice (Proprietary) Limited
+gotv
+
+// graphics : 2013-09-13 Over Madison, LLC
+graphics
+
+// gratis : 2014-03-20 Pioneer Tigers, LLC
+gratis
+
+// green : 2014-05-08 Afilias Limited
+green
+
+// gripe : 2014-03-06 Corn Sunset, LLC
+gripe
+
+// group : 2014-08-15 Romeo Town, LLC
+group
+
+// gucci : 2014-11-13 Guccio Gucci S.p.a.
+gucci
+
+// guge : 2014-08-28 Charleston Road Registry Inc.
+guge
+
+// guide : 2013-09-13 Snow Moon, LLC
+guide
+
+// guitars : 2013-11-14 Uniregistry, Corp.
+guitars
+
+// guru : 2013-08-27 Pioneer Cypress, LLC
+guru
+
+// hamburg : 2014-02-20 Hamburg Top-Level-Domain GmbH
+hamburg
+
+// hangout : 2014-11-13 Charleston Road Registry Inc.
+hangout
+
+// haus : 2013-12-05
+haus
+
+// hdfcbank : 2015-02-12 HDFC Bank Limited
+hdfcbank
+
+// health : 2015-02-11 DotHealth, LLC
+health
+
+// healthcare : 2014-06-12 Silver Glen, LLC
+healthcare
+
+// help : 2014-06-26 Uniregistry, Corp.
+help
+
+// helsinki : 2015-02-05 City of Helsinki
+helsinki
+
+// here : 2014-02-06 Charleston Road Registry Inc.
+here
+
+// hermes : 2014-07-10 HERMES INTERNATIONAL
+hermes
+
+// hiphop : 2014-03-06 Uniregistry, Corp.
+hiphop
+
+// hitachi : 2014-10-31 Hitachi, Ltd.
+hitachi
+
+// hiv : 2014-03-13 dotHIV gemeinnuetziger e.V.
+hiv
+
+// hockey : 2015-03-19 Half Willow, LLC
+hockey
+
+// holdings : 2013-08-27 John Madison, LLC
+holdings
+
+// holiday : 2013-11-07 Goose Woods, LLC
+holiday
+
+// homedepot : 2015-04-02 Homer TLC, Inc.
+homedepot
+
+// homes : 2014-01-09 DERHomes, LLC
+homes
+
+// honda : 2014-12-18 Honda Motor Co., Ltd.
+honda
+
+// horse : 2013-11-21 Top Level Domain Holdings Limited
+horse
+
+// host : 2014-04-17 DotHost Inc.
+host
+
+// hosting : 2014-05-29 Uniregistry, Corp.
+hosting
+
+// hoteles : 2015-03-05 Travel Reservations SRL
+hoteles
+
+// hotmail : 2014-12-18 Microsoft Corporation
+hotmail
+
+// house : 2013-11-07 Sugar Park, LLC
+house
+
+// how : 2014-01-23 Charleston Road Registry Inc.
+how
+
+// hsbc : 2014-10-24 HSBC Holdings PLC
+hsbc
+
+// htc : 2015-04-02 HTC corporation
+htc
+
+// ibm : 2014-07-31 International Business Machines Corporation
+ibm
+
+// icbc : 2015-02-19 Industrial and Commercial Bank of China Limited
+icbc
+
+// ice : 2014-10-30 IntercontinentalExchange, Inc.
+ice
+
+// icu : 2015-01-08 One.com A/S
+icu
+
+// ifm : 2014-01-30 ifm electronic gmbh
+ifm
+
+// iinet : 2014-07-03 Connect West Pty. Ltd.
+iinet
+
+// immo : 2014-07-10 Auburn Bloom, LLC
+immo
+
+// immobilien : 2013-11-07 United TLD Holdco Ltd.
+immobilien
+
+// industries : 2013-12-05 Outer House, LLC
+industries
+
+// infiniti : 2014-03-27 NISSAN MOTOR CO., LTD.
+infiniti
+
+// ing : 2014-01-23 Charleston Road Registry Inc.
+ing
+
+// ink : 2013-12-05 Top Level Design, LLC
+ink
+
+// institute : 2013-11-07 Outer Maple, LLC
+institute
+
+// insurance : 2015-02-19 fTLD Registry Services LLC
+insurance
+
+// insure : 2014-03-20 Pioneer Willow, LLC
+insure
+
+// international : 2013-11-07 Wild Way, LLC
+international
+
+// investments : 2014-03-20 Holly Glen, LLC
+investments
+
+// ipiranga : 2014-08-28 Ipiranga Produtos de Petroleo S.A.
+ipiranga
+
+// irish : 2014-08-07 Dot-Irish LLC
+irish
+
+// iselect : 2015-02-11 iSelect Ltd
+iselect
+
+// ist : 2014-08-28 Istanbul Metropolitan Municipality
+ist
+
+// istanbul : 2014-08-28 Istanbul Metropolitan Municipality
+istanbul
+
+// itau : 2014-10-02 Itau Unibanco Holding S.A.
+itau
+
+// iwc : 2014-06-23 Richemont DNS Inc.
+iwc
+
+// jaguar : 2014-11-13 Jaguar Land Rover Ltd
+jaguar
+
+// java : 2014-06-19 Oracle Corporation
+java
+
+// jcb : 2014-11-20 JCB Co., Ltd.
+jcb
+
+// jetzt : 2014-01-09 New TLD Company AB
+jetzt
+
+// jewelry : 2015-03-05 Wild Bloom, LLC
+jewelry
+
+// jio : 2015-04-02 Affinity Names, Inc.
+jio
+
+// jlc : 2014-12-04 Richemont DNS Inc.
+jlc
+
+// jll : 2015-04-02 Jones Lang LaSalle Incorporated
+jll
+
+// jmp : 2015-03-26 Matrix IP LLC
+jmp
+
+// joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
+joburg
+
+// jot : 2014-12-18 Amazon EU S.à r.l.
+jot
+
+// joy : 2014-12-18 Amazon EU S.à r.l.
+joy
+
+// jprs : 2014-09-18 Japan Registry Services Co., Ltd.
+jprs
+
+// juegos : 2014-03-20 Uniregistry, Corp.
+juegos
+
+// kaufen : 2013-11-07 United TLD Holdco Ltd.
+kaufen
+
+// kddi : 2014-09-12 KDDI CORPORATION
+kddi
+
+// kfh : 2014-12-04 Kuwait Finance House
+kfh
+
+// kim : 2013-09-23 Afilias Limited
+kim
+
+// kinder : 2014-11-07 Ferrero Trading Lux S.A.
+kinder
+
+// kitchen : 2013-09-20 Just Goodbye, LLC
+kitchen
+
+// kiwi : 2013-09-20 DOT KIWI LIMITED
+kiwi
+
+// koeln : 2014-01-09 NetCologne Gesellschaft für Telekommunikation mbH
+koeln
+
+// komatsu : 2015-01-08 Komatsu Ltd.
+komatsu
+
+// kpn : 2015-01-08 Koninklijke KPN N.V.
+kpn
+
+// krd : 2013-12-05 KRG Department of Information Technology
+krd
+
+// kred : 2013-12-19 KredTLD Pty Ltd
+kred
+
+// kyknet : 2015-03-05 Electronic Media Network (Pty) Ltd
+kyknet
+
+// kyoto : 2014-11-07 Academic Institution: Kyoto Jyoho Gakuen
+kyoto
+
+// lacaixa : 2014-01-09 CAIXA D'ESTALVIS I PENSIONS DE BARCELONA
+lacaixa
+
+// lancaster : 2015-02-12 LANCASTER
+lancaster
+
+// land : 2013-09-10 Pine Moon, LLC
+land
+
+// landrover : 2014-11-13 Jaguar Land Rover Ltd
+landrover
+
+// lasalle : 2015-04-02 Jones Lang LaSalle Incorporated
+lasalle
+
+// lat : 2014-10-16 ECOM-LAC Federaciòn de Latinoamèrica y el Caribe para Internet y el Comercio Electrònico
+lat
+
+// latrobe : 2014-06-16 La Trobe University
+latrobe
+
+// law : 2015-01-22 Minds + Machines Group Limited
+law
+
+// lawyer : 2014-03-20
+lawyer
+
+// lds : 2014-03-20 IRI Domain Management, LLC (\
+lds
+
+// lease : 2014-03-06 Victor Trail, LLC
+lease
+
+// leclerc : 2014-08-07 A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc
+leclerc
+
+// legal : 2014-10-16 Blue Falls, LLC
+legal
+
+// lgbt : 2014-05-08 Afilias Limited
+lgbt
+
+// liaison : 2014-10-02 Liaison Technologies, Incorporated
+liaison
+
+// lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
+lidl
+
+// life : 2014-02-06 Trixy Oaks, LLC
+life
+
+// lifeinsurance : 2015-01-15 American Council of Life Insurers
+lifeinsurance
+
+// lifestyle : 2014-12-11 Lifestyle Domain Holdings, Inc.
+lifestyle
+
+// lighting : 2013-08-27 John McCook, LLC
+lighting
+
+// like : 2014-12-18 Amazon EU S.à r.l.
+like
+
+// limited : 2014-03-06 Big Fest, LLC
+limited
+
+// limo : 2013-10-17 Hidden Frostbite, LLC
+limo
+
+// lincoln : 2014-11-13 Ford Motor Company
+lincoln
+
+// linde : 2014-12-04 Linde Aktiengesellschaft
+linde
+
+// link : 2013-11-14 Uniregistry, Corp.
+link
+
+// live : 2014-12-04 Half Woods, LLC
+live
+
+// lixil : 2015-03-19 LIXIL Group Corporation
+lixil
+
+// loan : 2014-11-20 dot Loan Limited
+loan
+
+// loans : 2014-03-20 June Woods, LLC
+loans
+
+// lol : 2015-01-30 Uniregistry, Corp.
+lol
+
+// london : 2013-11-14 Dot London Domains Limited
+london
+
+// lotte : 2014-11-07 Lotte Holdings Co., Ltd.
+lotte
+
+// lotto : 2014-04-10 Afilias Limited
+lotto
+
+// love : 2014-12-22 Merchant Law Group LLP
+love
+
+// ltd : 2014-09-25 Over Corner, LLC
+ltd
+
+// ltda : 2014-04-17 DOMAIN ROBOT SERVICOS DE HOSPEDAGEM NA INTERNET LTDA
+ltda
+
+// lupin : 2014-11-07 LUPIN LIMITED
+lupin
+
+// luxe : 2014-01-09 Top Level Domain Holdings Limited
+luxe
+
+// luxury : 2013-10-17 Luxury Partners, LLC
+luxury
+
+// madrid : 2014-05-01 Comunidad de Madrid
+madrid
+
+// maif : 2014-10-02 Mutuelle Assurance Instituteur France (MAIF)
+maif
+
+// maison : 2013-12-05 Victor Frostbite, LLC
+maison
+
+// makeup : 2015-01-15 L'Oréal
+makeup
+
+// man : 2014-12-04 MAN SE
+man
+
+// management : 2013-11-07 John Goodbye, LLC
+management
+
+// mango : 2013-10-24 PUNTO FA S.L.
+mango
+
+// market : 2014-03-06
+market
+
+// marketing : 2013-11-07 Fern Pass, LLC
+marketing
+
+// markets : 2014-12-11 IG Group Holdings PLC
+markets
+
+// marriott : 2014-10-09 Marriott Worldwide Corporation
+marriott
+
+// mba : 2015-04-02 Lone Hollow, LLC
+mba
+
+// media : 2014-03-06 Grand Glen, LLC
+media
+
+// meet : 2014-01-16
+meet
+
+// melbourne : 2014-05-29 The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
+melbourne
+
+// meme : 2014-01-30 Charleston Road Registry Inc.
+meme
+
+// memorial : 2014-10-16 Dog Beach, LLC
+memorial
+
+// men : 2015-02-26 Exclusive Registry Limited
+men
+
+// menu : 2013-09-11 Wedding TLD2, LLC
+menu
+
+// meo : 2014-11-07 PT Comunicacoes S.A.
+meo
+
+// miami : 2013-12-19 Top Level Domain Holdings Limited
+miami
+
+// microsoft : 2014-12-18 Microsoft Corporation
+microsoft
+
+// mini : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
+mini
+
+// mma : 2014-11-07 MMA IARD
+mma
+
+// mnet : 2015-03-05 Electronic Media Network (Pty) Ltd
+mnet
+
+// mobily : 2014-12-18 GreenTech Consultancy Company W.L.L.
+mobily
+
+// moda : 2013-11-07 United TLD Holdco Ltd.
+moda
+
+// moe : 2013-11-13 Interlink Co., Ltd.
+moe
+
+// moi : 2014-12-18 Amazon EU S.à r.l.
+moi
+
+// monash : 2013-09-30 Monash University
+monash
+
+// money : 2014-10-16 Outer McCook, LLC
+money
+
+// montblanc : 2014-06-23 Richemont DNS Inc.
+montblanc
+
+// mormon : 2013-12-05 IRI Domain Management, LLC (\
+mormon
+
+// mortgage : 2014-03-20
+mortgage
+
+// moscow : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+moscow
+
+// motorcycles : 2014-01-09 DERMotorcycles, LLC
+motorcycles
+
+// mov : 2014-01-30 Charleston Road Registry Inc.
+mov
+
+// movie : 2015-02-05 New Frostbite, LLC
+movie
+
+// movistar : 2014-10-16 Telefónica S.A.
+movistar
+
+// mtn : 2014-12-04 MTN Dubai Limited
+mtn
+
+// mtpc : 2014-11-20 Mitsubishi Tanabe Pharma Corporation
+mtpc
+
+// mtr : 2015-03-12 MTR Corporation Limited
+mtr
+
+// multichoice : 2015-03-12 MultiChoice (Proprietary) Limited
+multichoice
+
+// mutual : 2015-04-02 Northwestern Mutual MU TLD Registry, LLC
+mutual
+
+// mzansimagic : 2015-03-05 Electronic Media Network (Pty) Ltd
+mzansimagic
+
+// nadex : 2014-12-11 IG Group Holdings PLC
+nadex
+
+// nagoya : 2013-10-24 GMO Registry, Inc.
+nagoya
+
+// naspers : 2015-02-12 Intelprop (Proprietary) Limited
+naspers
+
+// natura : 2015-03-12 NATURA COSMÉTICOS S.A.
+natura
+
+// navy : 2014-03-06 United TLD Holdco Ltd.
+navy
+
+// nec : 2015-01-08 NEC Corporation
+nec
+
+// netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
+netbank
+
+// network : 2013-11-14 Trixy Manor, LLC
+network
+
+// neustar : 2013-12-05 NeuStar, Inc.
+neustar
+
+// new : 2014-01-30 Charleston Road Registry Inc.
+new
+
+// news : 2014-12-18
+news
+
+// nexus : 2014-07-24 Charleston Road Registry Inc.
+nexus
+
+// ngo : 2014-03-06 Public Interest Registry
+ngo
+
+// nhk : 2014-02-13 Japan Broadcasting Corporation (NHK)
+nhk
+
+// nico : 2014-12-04 DWANGO Co., Ltd.
+nico
+
+// ninja : 2013-11-07 United TLD Holdco Ltd.
+ninja
+
+// nissan : 2014-03-27 NISSAN MOTOR CO., LTD.
+nissan
+
+// nokia : 2015-01-08 Nokia Corporation
+nokia
+
+// norton : 2014-12-04 Symantec Corporation
+norton
+
+// nowruz : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+nowruz
+
+// nra : 2014-05-22 NRA Holdings Company, INC.
+nra
+
+// nrw : 2013-11-21 Minds + Machines GmbH
+nrw
+
+// ntt : 2014-10-31 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ntt
+
+// nyc : 2014-01-23 The City of New York by and through the New York City Department of Information Technology & Telecommunications
+nyc
+
+// obi : 2014-09-25 OBI Group Holding SE & Co. KGaA
+obi
+
+// office : 2015-03-12 Microsoft Corporation
+office
+
+// okinawa : 2013-12-05 BusinessRalliart Inc.
+okinawa
+
+// omega : 2015-01-08 The Swatch Group Ltd
+omega
+
+// one : 2014-11-07 One.com A/S
+one
+
+// ong : 2014-03-06 Public Interest Registry
+ong
+
+// onl : 2013-09-16 I-Registry Ltd.
+onl
+
+// online : 2015-01-15 DotOnline Inc.
+online
+
+// ooo : 2014-01-09 INFIBEAM INCORPORATION LIMITED
+ooo
+
+// oracle : 2014-06-19 Oracle Corporation
+oracle
+
+// orange : 2015-03-12 Orange Brand Services Limited
+orange
+
+// organic : 2014-03-27 Afilias Limited
+organic
+
+// orientexpress : 2015-02-05 Belmond Ltd.
+orientexpress
+
+// osaka : 2014-09-04 Interlink Co., Ltd.
+osaka
+
+// otsuka : 2013-10-11 Otsuka Holdings Co., Ltd.
+otsuka
+
+// ovh : 2014-01-16 OVH SAS
+ovh
+
+// page : 2014-12-04 Charleston Road Registry Inc.
+page
+
+// pamperedchef : 2015-02-05 The Pampered Chef, Ltd.
+pamperedchef
+
+// panerai : 2014-11-07 Richemont DNS Inc.
+panerai
+
+// paris : 2014-01-30 City of Paris
+paris
+
+// pars : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+pars
+
+// partners : 2013-12-05 Magic Glen, LLC
+partners
+
+// parts : 2013-12-05 Sea Goodbye, LLC
+parts
+
+// party : 2014-09-11 Blue Sky Registry Limited
+party
+
+// passagens : 2015-03-05 Travel Reservations SRL
+passagens
+
+// payu : 2015-02-12 MIH PayU B.V.
+payu
+
+// pharmacy : 2014-06-19 National Association of Boards of Pharmacy
+pharmacy
+
+// philips : 2014-11-07 Koninklijke Philips N.V.
+philips
+
+// photo : 2013-11-14 Uniregistry, Corp.
+photo
+
+// photography : 2013-09-20 Sugar Glen, LLC
+photography
+
+// photos : 2013-10-17 Sea Corner, LLC
+photos
+
+// physio : 2014-05-01 PhysBiz Pty Ltd
+physio
+
+// piaget : 2014-10-16 Richemont DNS Inc.
+piaget
+
+// pics : 2013-11-14 Uniregistry, Corp.
+pics
+
+// pictet : 2014-06-26 Pictet Europe S.A.
+pictet
+
+// pictures : 2014-03-06 Foggy Sky, LLC
+pictures
+
+// pid : 2015-01-08 Top Level Spectrum, Inc.
+pid
+
+// pin : 2014-12-18 Amazon EU S.à r.l.
+pin
+
+// pink : 2013-10-01 Afilias Limited
+pink
+
+// pizza : 2014-06-26 Foggy Moon, LLC
+pizza
+
+// place : 2014-04-24 Snow Galley, LLC
+place
+
+// play : 2015-03-05 Charleston Road Registry Inc.
+play
+
+// plumbing : 2013-09-10 Spring Tigers, LLC
+plumbing
+
+// plus : 2015-02-05 Sugar Mill, LLC
+plus
+
+// pohl : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+pohl
+
+// poker : 2014-07-03 Afilias Domains No. 5 Limited
+poker
+
+// porn : 2014-10-16 ICM Registry PN LLC
+porn
+
+// praxi : 2013-12-05 Praxi S.p.A.
+praxi
+
+// press : 2014-04-03 DotPress Inc.
+press
+
+// prod : 2014-01-23 Charleston Road Registry Inc.
+prod
+
+// productions : 2013-12-05 Magic Birch, LLC
+productions
+
+// prof : 2014-07-24 Charleston Road Registry Inc.
+prof
+
+// promo : 2014-12-18 Play.PROMO Oy
+promo
+
+// properties : 2013-12-05 Big Pass, LLC
+properties
+
+// property : 2014-05-22 Uniregistry, Corp.
+property
+
+// pub : 2013-12-12 United TLD Holdco Ltd.
+pub
+
+// qpon : 2013-11-14 dotCOOL, Inc.
+qpon
+
+// quebec : 2013-12-19 PointQuébec Inc
+quebec
+
+// quest : 2015-03-26 Quest ION Limited
+quest
+
+// racing : 2014-12-04 Premier Registry Limited
+racing
+
+// read : 2014-12-18 Amazon EU S.à r.l.
+read
+
+// realtor : 2014-05-29 Real Estate Domains LLC
+realtor
+
+// realty : 2015-03-19 Fegistry, LLC
+realty
+
+// recipes : 2013-10-17 Grand Island, LLC
+recipes
+
+// red : 2013-11-07 Afilias Limited
+red
+
+// redstone : 2014-10-31 Redstone Haute Couture Co., Ltd.
+redstone
+
+// redumbrella : 2015-03-26 Travelers TLD, LLC
+redumbrella
+
+// rehab : 2014-03-06 United TLD Holdco Ltd.
+rehab
+
+// reise : 2014-03-13 dotreise GmbH
+reise
+
+// reisen : 2014-03-06 New Cypress, LLC
+reisen
+
+// reit : 2014-09-04 National Association of Real Estate Investment Trusts, Inc.
+reit
+
+// reliance : 2015-04-02 Reliance Industries Limited
+reliance
+
+// ren : 2013-12-12 Beijing Qianxiang Wangjing Technology Development Co., Ltd.
+ren
+
+// rent : 2014-12-04 DERRent, LLC
+rent
+
+// rentals : 2013-12-05 Big Hollow,LLC
+rentals
+
+// repair : 2013-11-07 Lone Sunset, LLC
+repair
+
+// report : 2013-12-05 Binky Glen, LLC
+report
+
+// republican : 2014-03-20 United TLD Holdco Ltd.
+republican
+
+// rest : 2013-12-19 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+rest
+
+// restaurant : 2014-07-03 Snow Avenue, LLC
+restaurant
+
+// review : 2014-11-20 dot Review Limited
+review
+
+// reviews : 2013-09-13
+reviews
+
+// rich : 2013-11-21 I-Registry Ltd.
+rich
+
+// ricoh : 2014-11-20 Ricoh Company, Ltd.
+ricoh
+
+// ril : 2015-04-02 Reliance Industries Limited
+ril
+
+// rio : 2014-02-27 Empresa Municipal de Informática SA - IPLANRIO
+rio
+
+// rip : 2014-07-10 United TLD Holdco Ltd.
+rip
+
+// rocher : 2014-12-18 Ferrero Trading Lux S.A.
+rocher
+
+// rocks : 2013-11-14
+rocks
+
+// rodeo : 2013-12-19 Top Level Domain Holdings Limited
+rodeo
+
+// room : 2014-12-18 Amazon EU S.à r.l.
+room
+
+// rsvp : 2014-05-08 Charleston Road Registry Inc.
+rsvp
+
+// ruhr : 2013-10-02 regiodot GmbH & Co. KG
+ruhr
+
+// run : 2015-03-19 Snow Park, LLC
+run
+
+// rwe : 2015-04-02 RWE AG
+rwe
+
+// ryukyu : 2014-01-09 BusinessRalliart Inc.
+ryukyu
+
+// saarland : 2013-12-12 dotSaarland GmbH
+saarland
+
+// safe : 2014-12-18 Amazon EU S.à r.l.
+safe
+
+// safety : 2015-01-08 Safety Registry Services, LLC.
+safety
+
+// sakura : 2014-12-18 SAKURA Internet Inc.
+sakura
+
+// sale : 2014-10-16
+sale
+
+// salon : 2014-12-11 Outer Orchard, LLC
+salon
+
+// samsung : 2014-04-03 SAMSUNG SDS CO., LTD
+samsung
+
+// sandvik : 2014-11-13 Sandvik AB
+sandvik
+
+// sandvikcoromant : 2014-11-07 Sandvik AB
+sandvikcoromant
+
+// sanofi : 2014-10-09 Sanofi
+sanofi
+
+// sap : 2014-03-27 SAP AG
+sap
+
+// sapo : 2014-11-07 PT Comunicacoes S.A.
+sapo
+
+// sarl : 2014-07-03 Delta Orchard, LLC
+sarl
+
+// sas : 2015-04-02 Research IP LLC
+sas
+
+// saxo : 2014-10-31 Saxo Bank A/S
+saxo
+
+// sbi : 2015-03-12 STATE BANK OF INDIA
+sbi
+
+// sbs : 2014-11-07 SPECIAL BROADCASTING SERVICE CORPORATION
+sbs
+
+// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ)
+sca
+
+// scb : 2014-02-20 The Siam Commercial Bank Public Company Limited (\
+scb
+
+// schmidt : 2014-04-03 SALM S.A.S.
+schmidt
+
+// scholarships : 2014-04-24 Scholarships.com, LLC
+scholarships
+
+// school : 2014-12-18 Little Galley, LLC
+school
+
+// schule : 2014-03-06 Outer Moon, LLC
+schule
+
+// schwarz : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
+schwarz
+
+// science : 2014-09-11 dot Science Limited
+science
+
+// scor : 2014-10-31 SCOR SE
+scor
+
+// scot : 2014-01-23 Dot Scot Registry Limited
+scot
+
+// seat : 2014-05-22 SEAT, S.A. (Sociedad Unipersonal)
+seat
+
+// seek : 2014-12-04 Seek Limited
+seek
+
+// sener : 2014-10-24 Sener Ingeniería y Sistemas, S.A.
+sener
+
+// services : 2014-02-27 Fox Castle, LLC
+services
+
+// sew : 2014-07-17 SEW-EURODRIVE GmbH & Co KG
+sew
+
+// sex : 2014-11-13 ICM Registry SX LLC
+sex
+
+// sexy : 2013-09-11 Uniregistry, Corp.
+sexy
+
+// sharp : 2014-05-01 Sharp Corporation
+sharp
+
+// shia : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+shia
+
+// shiksha : 2013-11-14 Afilias Limited
+shiksha
+
+// shoes : 2013-10-02 Binky Galley, LLC
+shoes
+
+// shouji : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+shouji
+
+// show : 2015-03-05 Snow Beach, LLC
+show
+
+// shriram : 2014-01-23 Shriram Capital Ltd.
+shriram
+
+// sina : 2015-03-12 Sina Corporation
+sina
+
+// singles : 2013-08-27 Fern Madison, LLC
+singles
+
+// site : 2015-01-15 DotSite Inc.
+site
+
+// skin : 2015-01-15 L'Oréal
+skin
+
+// sky : 2014-06-19 Sky IP International Ltd, a company incorporated in England and Wales, operating via its registered Swiss branch
+sky
+
+// skype : 2014-12-18 Microsoft Corporation
+skype
+
+// smile : 2014-12-18 Amazon EU S.à r.l.
+smile
+
+// sncf : 2015-02-19 Société Nationale des Chemins de fer Francais S N C F
+sncf
+
+// soccer : 2015-03-26 Foggy Shadow, LLC
+soccer
+
+// social : 2013-11-07 United TLD Holdco Ltd.
+social
+
+// software : 2014-03-20
+software
+
+// sohu : 2013-12-19 Sohu.com Limited
+sohu
+
+// solar : 2013-11-07 Ruby Town, LLC
+solar
+
+// solutions : 2013-11-07 Silver Cover, LLC
+solutions
+
+// song : 2015-02-26 Amazon EU S.à r.l.
+song
+
+// sony : 2015-01-08 Sony Corporation
+sony
+
+// soy : 2014-01-23 Charleston Road Registry Inc.
+soy
+
+// space : 2014-04-03 DotSpace Inc.
+space
+
+// spiegel : 2014-02-05 SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG
+spiegel
+
+// spot : 2015-02-26 Amazon EU S.à r.l.
+spot
+
+// spreadbetting : 2014-12-11 IG Group Holdings PLC
+spreadbetting
+
+// stada : 2014-11-13 STADA Arzneimittel AG
+stada
+
+// star : 2015-01-08 Star India Private Limited
+star
+
+// starhub : 2015-02-05 StarHub Limited
+starhub
+
+// statebank : 2015-03-12 STATE BANK OF INDIA
+statebank
+
+// statoil : 2014-12-04 Statoil ASA
+statoil
+
+// stc : 2014-10-09 Saudi Telecom Company
+stc
+
+// stcgroup : 2014-10-09 Saudi Telecom Company
+stcgroup
+
+// stockholm : 2014-12-18 Stockholms kommun
+stockholm
+
+// storage : 2014-12-22 Self Storage Company LLC
+storage
+
+// studio : 2015-02-11 Spring Goodbye, LLC
+studio
+
+// study : 2014-12-11 OPEN UNIVERSITIES AUSTRALIA PTY LTD
+study
+
+// style : 2014-12-04 Binky Moon, LLC
+style
+
+// sucks : 2014-12-22 Vox Populi Registry Inc.
+sucks
+
+// supersport : 2015-03-05 SuperSport International Holdings Proprietary Limited
+supersport
+
+// supplies : 2013-12-19 Atomic Fields, LLC
+supplies
+
+// supply : 2013-12-19 Half Falls, LLC
+supply
+
+// support : 2013-10-24 Grand Orchard, LLC
+support
+
+// surf : 2014-01-09 Top Level Domain Holdings Limited
+surf
+
+// surgery : 2014-03-20 Tin Avenue, LLC
+surgery
+
+// suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION
+suzuki
+
+// swatch : 2015-01-08 The Swatch Group Ltd
+swatch
+
+// swiss : 2014-10-16 Swiss Confederation
+swiss
+
+// sydney : 2014-09-18 State of New South Wales, Department of Premier and Cabinet
+sydney
+
+// symantec : 2014-12-04 Symantec Corporation
+symantec
+
+// systems : 2013-11-07 Dash Cypress, LLC
+systems
+
+// tab : 2014-12-04 Tabcorp Holdings Limited
+tab
+
+// taipei : 2014-07-10 Taipei City Government
+taipei
+
+// taobao : 2015-01-15 Alibaba Group Holding Limited
+taobao
+
+// tatamotors : 2015-03-12 Tata Motors Ltd
+tatamotors
+
+// tatar : 2014-04-24 Limited Liability Company \
+tatar
+
+// tattoo : 2013-08-30 Uniregistry, Corp.
+tattoo
+
+// tax : 2014-03-20 Storm Orchard, LLC
+tax
+
+// taxi : 2015-03-19 Pine Falls, LLC
+taxi
+
+// tci : 2014-09-12 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+tci
+
+// team : 2015-03-05 Atomic Lake, LLC
+team
+
+// tech : 2015-01-30 Dot Tech LLC
+tech
+
+// technology : 2013-09-13 Auburn Falls
+technology
+
+// telecity : 2015-02-19 TelecityGroup International Limited
+telecity
+
+// telefonica : 2014-10-16 Telefónica S.A.
+telefonica
+
+// temasek : 2014-08-07 Temasek Holdings (Private) Limited
+temasek
+
+// tennis : 2014-12-04 Cotton Bloom, LLC
+tennis
+
+// thd : 2015-04-02 Homer TLC, Inc.
+thd
+
+// theater : 2015-03-19 Blue Tigers, LLC
+theater
+
+// tickets : 2015-02-05 Accent Media Limited
+tickets
+
+// tienda : 2013-11-14 Victor Manor, LLC
+tienda
+
+// tiffany : 2015-01-30 Tiffany and Company
+tiffany
+
+// tips : 2013-09-20 Corn Willow, LLC
+tips
+
+// tires : 2014-11-07 Dog Edge, LLC
+tires
+
+// tirol : 2014-04-24 punkt Tirol GmbH
+tirol
+
+// tmall : 2015-01-15 Alibaba Group Holding Limited
+tmall
+
+// today : 2013-09-20 Pearl Woods, LLC
+today
+
+// tokyo : 2013-11-13 GMO Registry, Inc.
+tokyo
+
+// tools : 2013-11-21 Pioneer North, LLC
+tools
+
+// top : 2014-03-20 Jiangsu Bangning Science & Technology Co.,Ltd.
+top
+
+// toray : 2014-12-18 Toray Industries, Inc.
+toray
+
+// toshiba : 2014-04-10 TOSHIBA Corporation
+toshiba
+
+// tours : 2015-01-22 Sugar Station, LLC
+tours
+
+// town : 2014-03-06 Koko Moon, LLC
+town
+
+// toys : 2014-03-06 Pioneer Orchard, LLC
+toys
+
+// trade : 2014-01-23 Elite Registry Limited
+trade
+
+// trading : 2014-12-11 IG Group Holdings PLC
+trading
+
+// training : 2013-11-07 Wild Willow, LLC
+training
+
+// travelers : 2015-03-26 Travelers TLD, LLC
+travelers
+
+// travelersinsurance : 2015-03-26 Travelers TLD, LLC
+travelersinsurance
+
+// trust : 2014-10-16
+trust
+
+// trv : 2015-03-26 Travelers TLD, LLC
+trv
+
+// tui : 2014-07-03 TUI AG
+tui
+
+// tunes : 2015-02-26 Amazon EU S.à r.l.
+tunes
+
+// tushu : 2014-12-18 Amazon EU S.à r.l.
+tushu
+
+// tvs : 2015-02-19 T V SUNDRAM IYENGAR & SONS LIMITED
+tvs
+
+// ubs : 2014-12-11 UBS AG
+ubs
+
+// university : 2014-03-06 Little Station, LLC
+university
+
+// uno : 2013-09-11 Dot Latin LLC
+uno
+
+// uol : 2014-05-01 UBN INTERNET LTDA.
+uol
+
+// vacations : 2013-12-05 Atomic Tigers, LLC
+vacations
+
+// vana : 2014-12-11 Lifestyle Domain Holdings, Inc.
+vana
+
+// vegas : 2014-01-16 Dot Vegas, Inc.
+vegas
+
+// ventures : 2013-08-27 Binky Lake, LLC
+ventures
+
+// versicherung : 2014-03-20 dotversicherung-registry GmbH
+versicherung
+
+// vet : 2014-03-06
+vet
+
+// viajes : 2013-10-17 Black Madison, LLC
+viajes
+
+// video : 2014-10-16
+video
+
+// viking : 2015-04-02 Viking River Cruises (Bermuda) Ltd.
+viking
+
+// villas : 2013-12-05 New Sky, LLC
+villas
+
+// vip : 2015-01-22 Minds + Machines Group Limited
+vip
+
+// virgin : 2014-09-25 Virgin Enterprises Limited
+virgin
+
+// vision : 2013-12-05 Koko Station, LLC
+vision
+
+// vista : 2014-09-18 Vistaprint Limited
+vista
+
+// vistaprint : 2014-09-18 Vistaprint Limited
+vistaprint
+
+// viva : 2014-11-07 Saudi Telecom Company
+viva
+
+// vlaanderen : 2014-02-06 DNS.be vzw
+vlaanderen
+
+// vodka : 2013-12-19 Top Level Domain Holdings Limited
+vodka
+
+// vote : 2013-11-21 Monolith Registry LLC
+vote
+
+// voting : 2013-11-13 Valuetainment Corp.
+voting
+
+// voto : 2013-11-21 Monolith Registry LLC
+voto
+
+// voyage : 2013-08-27 Ruby House, LLC
+voyage
+
+// vuelos : 2015-03-05 Travel Reservations SRL
+vuelos
+
+// wales : 2014-05-08 Nominet UK
+wales
+
+// walter : 2014-11-13 Sandvik AB
+walter
+
+// wang : 2013-10-24 Zodiac Leo Limited
+wang
+
+// wanggou : 2014-12-18 Amazon EU S.à r.l.
+wanggou
+
+// watch : 2013-11-14 Sand Shadow, LLC
+watch
+
+// watches : 2014-12-22 Richemont DNS Inc.
+watches
+
+// weather : 2015-01-08 The Weather Channel, LLC
+weather
+
+// weatherchannel : 2015-03-12 The Weather Channel, LLC
+weatherchannel
+
+// webcam : 2014-01-23 dot Webcam Limited
+webcam
+
+// website : 2014-04-03 DotWebsite Inc.
+website
+
+// wed : 2013-10-01 Atgron, Inc.
+wed
+
+// wedding : 2014-04-24 Top Level Domain Holdings Limited
+wedding
+
+// weibo : 2015-03-05 Sina Corporation
+weibo
+
+// weir : 2015-01-29 Weir Group IP Limited
+weir
+
+// whoswho : 2014-02-20 Who's Who Registry
+whoswho
+
+// wien : 2013-10-28 punkt.wien GmbH
+wien
+
+// wiki : 2013-11-07 Top Level Design, LLC
+wiki
+
+// williamhill : 2014-03-13 William Hill Organization Limited
+williamhill
+
+// win : 2014-11-20 First Registry Limited
+win
+
+// windows : 2014-12-18 Microsoft Corporation
+windows
+
+// wme : 2014-02-13 William Morris Endeavor Entertainment, LLC
+wme
+
+// work : 2013-12-19 Top Level Domain Holdings Limited
+work
+
+// works : 2013-11-14 Little Dynamite, LLC
+works
+
+// world : 2014-06-12 Bitter Fields, LLC
+world
+
+// wtc : 2013-12-19 World Trade Centers Association, Inc.
+wtc
+
+// wtf : 2014-03-06 Hidden Way, LLC
+wtf
+
+// xbox : 2014-12-18 Microsoft Corporation
+xbox
+
+// xerox : 2014-10-24 Xerox DNHC LLC
+xerox
+
+// xihuan : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+xihuan
+
+// xin : 2014-12-11 Elegant Leader Limited
+xin
+
+// xn--11b4c3d : 2015-01-15 VeriSign Sarl
+कॉम
+
+// xn--1ck2e1b : 2015-02-26 Amazon EU S.à r.l.
+セール
+
+// xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd.
+佛山
+
+// xn--30rr7y : 2014-06-12 Excellent First Limited
+慈善
+
+// xn--3bst00m : 2013-09-13 Eagle Horizon Limited
+集团
+
+// xn--3ds443g : 2013-09-08 TLD REGISTRY LIMITED
+在线
+
+// xn--3pxu8k : 2015-01-15 VeriSign Sarl
+点看
+
+// xn--42c2d9a : 2015-01-15 VeriSign Sarl
+คอม
+
+// xn--45q11c : 2013-11-21 Zodiac Scorpio Limited
+八卦
+
+// xn--4gbrim : 2013-10-04 Suhub Electronic Establishment
+موقع
+
+// xn--55qw42g : 2013-11-08 China Organizational Name Administration Center
+公益
+
+// xn--55qx5d : 2013-11-14 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center)
+公司
+
+// xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited
+网站
+
+// xn--6frz82g : 2013-09-23 Afilias Limited
+移动
+
+// xn--6qq986b3xl : 2013-09-13 Tycoon Treasure Limited
+我爱你
+
+// xn--80adxhks : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+москва
+
+// xn--80asehdb : 2013-07-14 CORE Association
+онлайн
+
+// xn--80aswg : 2013-07-14 CORE Association
+сайт
+
+// xn--8y0a063a : 2015-03-26 China United Network Communications Corporation Limited
+联通
+
+// xn--9dbq2a : 2015-01-15 VeriSign Sarl
+קום
+
+// xn--9et52u : 2014-06-12 RISE VICTORY LIMITED
+时尚
+
+// xn--9krt00a : 2015-03-12 Sina Corporation
+微博
+
+// xn--b4w605ferd : 2014-08-07 Temasek Holdings (Private) Limited
+淡马锡
+
+// xn--bck1b9a5dre4c : 2015-02-26 Amazon EU S.à r.l.
+ファッション
+
+// xn--c1avg : 2013-11-14 Public Interest Registry
+орг
+
+// xn--c2br7g : 2015-01-15 VeriSign Sarl
+नेट
+
+// xn--cck2b3b : 2015-02-26 Amazon EU S.à r.l.
+ストア
+
+// xn--cg4bki : 2013-09-27 SAMSUNG SDS CO., LTD
+삼성
+
+// xn--czr694b : 2014-01-16 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY.HONGKONG LIMITED
+商标
+
+// xn--czrs0t : 2013-12-19 Wild Island, LLC
+商店
+
+// xn--czru2d : 2013-11-21 Zodiac Capricorn Limited
+商城
+
+// xn--d1acj3b : 2013-11-20 The Foundation for Network Initiatives “The Smart Internet”
+дети
+
+// xn--eckvdtc9d : 2014-12-18 Amazon EU S.à r.l.
+ポイント
+
+// xn--efvy88h : 2014-08-22 Xinhua News Agency Guangdong Branch 新华通讯社广东分社
+新闻
+
+// xn--estv75g : 2015-02-19 Industrial and Commercial Bank of China Limited
+工行
+
+// xn--fhbei : 2015-01-15 VeriSign Sarl
+كوم
+
+// xn--fiq228c5hs : 2013-09-08 TLD REGISTRY LIMITED
+中文网
+
+// xn--fiq64b : 2013-10-14 CITIC Group Corporation
+中信
+
+// xn--fjq720a : 2014-05-22 Will Bloom, LLC
+娱乐
+
+// xn--flw351e : 2014-07-31 Charleston Road Registry Inc.
+谷歌
+
+// xn--g2xx48c : 2015-01-30 Minds + Machines Group Limited
+购物
+
+// xn--gckr3f0f : 2015-02-26 Amazon EU S.à r.l.
+クラウド
+
+// xn--hxt814e : 2014-05-15 Zodiac Libra Limited
+网店
+
+// xn--i1b6b1a6a2e : 2013-11-14 Public Interest Registry
+संगठन
+
+// xn--imr513n : 2014-12-11 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED
+餐厅
+
+// xn--io0a7i : 2013-11-14 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center)
+网络
+
+// xn--j1aef : 2015-01-15 VeriSign Sarl
+ком
+
+// xn--jlq61u9w7b : 2015-01-08 Nokia Corporation
+诺基亚
+
+// xn--jvr189m : 2015-02-26 Amazon EU S.à r.l.
+食品
+
+// xn--kcrx77d1x4a : 2014-11-07 Koninklijke Philips N.V.
+飞利浦
+
+// xn--kpu716f : 2014-12-22 Richemont DNS Inc.
+手表
+
+// xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd
+手机
+
+// xn--mgba3a3ejt : 2014-11-20 Aramco Services Company
+ارامكو
+
+// xn--mgbab2bd : 2013-10-31 CORE Association
+بازار
+
+// xn--mgbb9fbpob : 2014-12-18 GreenTech Consultancy Company W.L.L.
+موبايلي
+
+// xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
+همراه
+
+// xn--mk1bu44c : 2015-01-15 VeriSign Sarl
+닷컴
+
+// xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd.
+政府
+
+// xn--ngbc5azd : 2013-07-13 International Domain Registry Pty. Ltd.
+شبكة
+
+// xn--ngbe9e0a : 2014-12-04 Kuwait Finance House
+بيتك
+
+// xn--nqv7f : 2013-11-14 Public Interest Registry
+机构
+
+// xn--nqv7fs00ema : 2013-11-14 Public Interest Registry
+组织机构
+
+// xn--nyqy26a : 2014-11-07 Stable Tone Limited
+健康
+
+// xn--p1acf : 2013-12-12 Rusnames Limited
+рус
+
+// xn--pbt977c : 2014-12-22 Richemont DNS Inc.
+珠宝
+
+// xn--pssy2u : 2015-01-15 VeriSign Sarl
+大拿
+
+// xn--q9jyb4c : 2013-09-17 Charleston Road Registry Inc.
+みんな
+
+// xn--qcka1pmc : 2014-07-31 Charleston Road Registry Inc.
+グーグル
+
+// xn--rhqv96g : 2013-09-11 Stable Tone Limited
+世界
+
+// xn--rovu88b : 2015-02-26 Amazon EU S.à r.l.
+書籍
+
+// xn--ses554g : 2014-01-16
+网址
+
+// xn--t60b56a : 2015-01-15 VeriSign Sarl
+닷넷
+
+// xn--tckwe : 2015-01-15 VeriSign Sarl
+コム
+
+// xn--unup4y : 2013-07-14 Spring Fields, LLC
+游戏
+
+// xn--vermgensberater-ctb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+vermögensberater
+
+// xn--vermgensberatung-pwb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
+vermögensberatung
+
+// xn--vhquv : 2013-08-27 Dash McCook, LLC
+企业
+
+// xn--vuq861b : 2014-10-16 Beijing Tele-info Network Technology Co., Ltd.
+信息
+
+// xn--xhq521b : 2013-11-14 Guangzhou YU Wei Information Technology Co., Ltd.
+广东
+
+// xn--zfr164b : 2013-11-08 China Organizational Name Administration Center
+政务
+
+// xyz : 2013-12-05 XYZ.COM LLC
+xyz
+
+// yachts : 2014-01-09 DERYachts, LLC
+yachts
+
+// yahoo : 2015-04-02 Yahoo! Domain Services Inc.
+yahoo
+
+// yamaxun : 2014-12-18 Amazon EU S.à r.l.
+yamaxun
+
+// yandex : 2014-04-10 YANDEX, LLC
+yandex
+
+// yodobashi : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
+yodobashi
+
+// yoga : 2014-05-29 Top Level Domain Holdings Limited
+yoga
+
+// yokohama : 2013-12-12 GMO Registry, Inc.
+yokohama
+
+// youtube : 2014-05-01 Charleston Road Registry Inc.
+youtube
+
+// yun : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+yun
+
+// zara : 2014-11-07 Industria de Diseño Textil, S.A. (INDITEX, S.A.)
+zara
+
+// zero : 2014-12-18 Amazon EU S.à r.l.
+zero
+
+// zip : 2014-05-08 Charleston Road Registry Inc.
+zip
+
+// zone : 2013-11-14 Outer Falls, LLC
+zone
+
+// zuerich : 2014-11-07 Kanton Zürich (Canton of Zurich)
+zuerich
+
+
+// ===END ICANN DOMAINS===
+// ===BEGIN PRIVATE DOMAINS===
+// (Note: these are in alphabetical order by company name)
+
+// Amazon CloudFront : https://aws.amazon.com/cloudfront/
+// Submitted by Donavan Miller <donavanm@amazon.com> 2013-03-22
+cloudfront.net
+
+// Amazon Elastic Compute Cloud: https://aws.amazon.com/ec2/
+// Submitted by Osman Surkatty <osmans@amazon.com> 2014-12-16
+ap-northeast-1.compute.amazonaws.com
+ap-southeast-1.compute.amazonaws.com
+ap-southeast-2.compute.amazonaws.com
+cn-north-1.compute.amazonaws.cn
+compute.amazonaws.cn
+compute.amazonaws.com
+compute-1.amazonaws.com
+eu-west-1.compute.amazonaws.com
+eu-central-1.compute.amazonaws.com
+sa-east-1.compute.amazonaws.com
+us-east-1.amazonaws.com
+us-gov-west-1.compute.amazonaws.com
+us-west-1.compute.amazonaws.com
+us-west-2.compute.amazonaws.com
+z-1.compute-1.amazonaws.com
+z-2.compute-1.amazonaws.com
+
+// Amazon Elastic Beanstalk : https://aws.amazon.com/elasticbeanstalk/
+// Submitted by Adam Stein <astein@amazon.com> 2013-04-02
+elasticbeanstalk.com
+
+// Amazon Elastic Load Balancing : https://aws.amazon.com/elasticloadbalancing/
+// Submitted by Scott Vidmar <svidmar@amazon.com> 2013-03-27
+elb.amazonaws.com
+
+// Amazon S3 : https://aws.amazon.com/s3/
+// Submitted by Courtney Eckhardt <coec@amazon.com> 2013-03-22
+s3.amazonaws.com
+s3-us-west-2.amazonaws.com
+s3-us-west-1.amazonaws.com
+s3-eu-west-1.amazonaws.com
+s3-ap-southeast-1.amazonaws.com
+s3-ap-southeast-2.amazonaws.com
+s3-ap-northeast-1.amazonaws.com
+s3-sa-east-1.amazonaws.com
+s3-us-gov-west-1.amazonaws.com
+s3-fips-us-gov-west-1.amazonaws.com
+s3-website-us-east-1.amazonaws.com
+s3-website-us-west-2.amazonaws.com
+s3-website-us-west-1.amazonaws.com
+s3-website-eu-west-1.amazonaws.com
+s3-website-ap-southeast-1.amazonaws.com
+s3-website-ap-southeast-2.amazonaws.com
+s3-website-ap-northeast-1.amazonaws.com
+s3-website-sa-east-1.amazonaws.com
+s3-website-us-gov-west-1.amazonaws.com
+
+// BetaInABox
+// Submitted by adrian@betainabox.com 2012-09-13
+betainabox.com
+
+// CentralNic : http://www.centralnic.com/names/domains
+// Submitted by registry <gavin.brown@centralnic.com> 2012-09-27
+ae.org
+ar.com
+br.com
+cn.com
+com.de
+com.se
+de.com
+eu.com
+gb.com
+gb.net
+hu.com
+hu.net
+jp.net
+jpn.com
+kr.com
+mex.com
+no.com
+qc.com
+ru.com
+sa.com
+se.com
+se.net
+uk.com
+uk.net
+us.com
+uy.com
+za.bz
+za.com
+
+// Africa.com Web Solutions Ltd : https://registry.africa.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+africa.com
+
+// iDOT Services Limited : http://www.domain.gr.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+gr.com
+
+// Radix FZC : http://domains.in.net
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+in.net
+
+// US REGISTRY LLC : http://us.org
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+us.org
+
+// co.com Registry, LLC : https://registry.co.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com> 2014-02-04
+co.com
+
+// c.la : http://www.c.la/
+c.la
+
+// cloudControl : https://www.cloudcontrol.com/
+// Submitted by Tobias Wilken <tw@cloudcontrol.com> 2013-07-23
+cloudcontrolled.com
+cloudcontrolapp.com
+
+// co.ca : http://registry.co.ca/
+co.ca
+
+// CoDNS B.V.
+co.nl
+co.no
+
+// Commerce Guys, SAS
+// Submitted by Damien Tournoud <damien@commerceguys.com> 2015-01-22
+*.platform.sh
+
+// Cupcake : https://cupcake.io/
+// Submitted by Jonathan Rudenberg <jonathan@cupcake.io> 2013-10-08
+cupcake.is
+
+// DreamHost : http://www.dreamhost.com/
+// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com> 2012-10-02
+dreamhosters.com
+
+// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/
+dyndns-at-home.com
+dyndns-at-work.com
+dyndns-blog.com
+dyndns-free.com
+dyndns-home.com
+dyndns-ip.com
+dyndns-mail.com
+dyndns-office.com
+dyndns-pics.com
+dyndns-remote.com
+dyndns-server.com
+dyndns-web.com
+dyndns-wiki.com
+dyndns-work.com
+dyndns.biz
+dyndns.info
+dyndns.org
+dyndns.tv
+at-band-camp.net
+ath.cx
+barrel-of-knowledge.info
+barrell-of-knowledge.info
+better-than.tv
+blogdns.com
+blogdns.net
+blogdns.org
+blogsite.org
+boldlygoingnowhere.org
+broke-it.net
+buyshouses.net
+cechire.com
+dnsalias.com
+dnsalias.net
+dnsalias.org
+dnsdojo.com
+dnsdojo.net
+dnsdojo.org
+does-it.net
+doesntexist.com
+doesntexist.org
+dontexist.com
+dontexist.net
+dontexist.org
+doomdns.com
+doomdns.org
+dvrdns.org
+dyn-o-saur.com
+dynalias.com
+dynalias.net
+dynalias.org
+dynathome.net
+dyndns.ws
+endofinternet.net
+endofinternet.org
+endoftheinternet.org
+est-a-la-maison.com
+est-a-la-masion.com
+est-le-patron.com
+est-mon-blogueur.com
+for-better.biz
+for-more.biz
+for-our.info
+for-some.biz
+for-the.biz
+forgot.her.name
+forgot.his.name
+from-ak.com
+from-al.com
+from-ar.com
+from-az.net
+from-ca.com
+from-co.net
+from-ct.com
+from-dc.com
+from-de.com
+from-fl.com
+from-ga.com
+from-hi.com
+from-ia.com
+from-id.com
+from-il.com
+from-in.com
+from-ks.com
+from-ky.com
+from-la.net
+from-ma.com
+from-md.com
+from-me.org
+from-mi.com
+from-mn.com
+from-mo.com
+from-ms.com
+from-mt.com
+from-nc.com
+from-nd.com
+from-ne.com
+from-nh.com
+from-nj.com
+from-nm.com
+from-nv.com
+from-ny.net
+from-oh.com
+from-ok.com
+from-or.com
+from-pa.com
+from-pr.com
+from-ri.com
+from-sc.com
+from-sd.com
+from-tn.com
+from-tx.com
+from-ut.com
+from-va.com
+from-vt.com
+from-wa.com
+from-wi.com
+from-wv.com
+from-wy.com
+ftpaccess.cc
+fuettertdasnetz.de
+game-host.org
+game-server.cc
+getmyip.com
+gets-it.net
+go.dyndns.org
+gotdns.com
+gotdns.org
+groks-the.info
+groks-this.info
+ham-radio-op.net
+here-for-more.info
+hobby-site.com
+hobby-site.org
+home.dyndns.org
+homedns.org
+homeftp.net
+homeftp.org
+homeip.net
+homelinux.com
+homelinux.net
+homelinux.org
+homeunix.com
+homeunix.net
+homeunix.org
+iamallama.com
+in-the-band.net
+is-a-anarchist.com
+is-a-blogger.com
+is-a-bookkeeper.com
+is-a-bruinsfan.org
+is-a-bulls-fan.com
+is-a-candidate.org
+is-a-caterer.com
+is-a-celticsfan.org
+is-a-chef.com
+is-a-chef.net
+is-a-chef.org
+is-a-conservative.com
+is-a-cpa.com
+is-a-cubicle-slave.com
+is-a-democrat.com
+is-a-designer.com
+is-a-doctor.com
+is-a-financialadvisor.com
+is-a-geek.com
+is-a-geek.net
+is-a-geek.org
+is-a-green.com
+is-a-guru.com
+is-a-hard-worker.com
+is-a-hunter.com
+is-a-knight.org
+is-a-landscaper.com
+is-a-lawyer.com
+is-a-liberal.com
+is-a-libertarian.com
+is-a-linux-user.org
+is-a-llama.com
+is-a-musician.com
+is-a-nascarfan.com
+is-a-nurse.com
+is-a-painter.com
+is-a-patsfan.org
+is-a-personaltrainer.com
+is-a-photographer.com
+is-a-player.com
+is-a-republican.com
+is-a-rockstar.com
+is-a-socialist.com
+is-a-soxfan.org
+is-a-student.com
+is-a-teacher.com
+is-a-techie.com
+is-a-therapist.com
+is-an-accountant.com
+is-an-actor.com
+is-an-actress.com
+is-an-anarchist.com
+is-an-artist.com
+is-an-engineer.com
+is-an-entertainer.com
+is-by.us
+is-certified.com
+is-found.org
+is-gone.com
+is-into-anime.com
+is-into-cars.com
+is-into-cartoons.com
+is-into-games.com
+is-leet.com
+is-lost.org
+is-not-certified.com
+is-saved.org
+is-slick.com
+is-uberleet.com
+is-very-bad.org
+is-very-evil.org
+is-very-good.org
+is-very-nice.org
+is-very-sweet.org
+is-with-theband.com
+isa-geek.com
+isa-geek.net
+isa-geek.org
+isa-hockeynut.com
+issmarterthanyou.com
+isteingeek.de
+istmein.de
+kicks-ass.net
+kicks-ass.org
+knowsitall.info
+land-4-sale.us
+lebtimnetz.de
+leitungsen.de
+likes-pie.com
+likescandy.com
+merseine.nu
+mine.nu
+misconfused.org
+mypets.ws
+myphotos.cc
+neat-url.com
+office-on-the.net
+on-the-web.tv
+podzone.net
+podzone.org
+readmyblog.org
+saves-the-whales.com
+scrapper-site.net
+scrapping.cc
+selfip.biz
+selfip.com
+selfip.info
+selfip.net
+selfip.org
+sells-for-less.com
+sells-for-u.com
+sells-it.net
+sellsyourhome.org
+servebbs.com
+servebbs.net
+servebbs.org
+serveftp.net
+serveftp.org
+servegame.org
+shacknet.nu
+simple-url.com
+space-to-rent.com
+stuff-4-sale.org
+stuff-4-sale.us
+teaches-yoga.com
+thruhere.net
+traeumtgerade.de
+webhop.biz
+webhop.info
+webhop.net
+webhop.org
+worse-than.tv
+writesthisblog.com
+
+// Fastly Inc. http://www.fastly.com/
+// Submitted by Vladimir Vuksan <vladimir@fastly.com> 2013-05-31
+a.ssl.fastly.net
+b.ssl.fastly.net
+global.ssl.fastly.net
+a.prod.fastly.net
+global.prod.fastly.net
+
+// Firebase, Inc.
+// Submitted by Chris Raynor <chris@firebase.com> 2014-01-21
+firebaseapp.com
+
+// Flynn : https://flynn.io
+// Submitted by Jonathan Rudenberg <jonathan@flynn.io> 2014-07-12
+flynnhub.com
+
+// GDS : https://www.gov.uk/service-manual/operations/operating-servicegovuk-subdomains
+// Submitted by David Illsley <david.illsley@digital.cabinet-office.gov.uk> 2014-08-28
+service.gov.uk
+
+// GitHub, Inc.
+// Submitted by Ben Toews <btoews@github.com> 2014-02-06
+github.io
+githubusercontent.com
+
+// GlobeHosting, Inc.
+// Submitted by Zoltan Egresi <egresi@globehosting.com> 2013-07-12
+ro.com
+
+// Google, Inc.
+// Submitted by Eduardo Vela <evn@google.com> 2014-12-19
+appspot.com
+blogspot.ae
+blogspot.be
+blogspot.bj
+blogspot.ca
+blogspot.cf
+blogspot.ch
+blogspot.co.at
+blogspot.co.il
+blogspot.co.nz
+blogspot.co.uk
+blogspot.com
+blogspot.com.ar
+blogspot.com.au
+blogspot.com.br
+blogspot.com.es
+blogspot.com.tr
+blogspot.cv
+blogspot.cz
+blogspot.de
+blogspot.dk
+blogspot.fi
+blogspot.fr
+blogspot.gr
+blogspot.hk
+blogspot.hu
+blogspot.ie
+blogspot.in
+blogspot.it
+blogspot.jp
+blogspot.kr
+blogspot.mr
+blogspot.mx
+blogspot.nl
+blogspot.no
+blogspot.pt
+blogspot.re
+blogspot.ro
+blogspot.ru
+blogspot.se
+blogspot.sg
+blogspot.sk
+blogspot.td
+blogspot.tw
+codespot.com
+googleapis.com
+googlecode.com
+pagespeedmobilizer.com
+withgoogle.com
+
+// Heroku : https://www.heroku.com/
+// Submitted by Tom Maher <tmaher@heroku.com> 2013-05-02
+herokuapp.com
+herokussl.com
+
+// iki.fi
+// Submitted by Hannu Aronsson <haa@iki.fi> 2009-11-05
+iki.fi
+
+// info.at : http://www.info.at/
+biz.at
+info.at
+
+// Michau Enterprises Limited : http://www.co.pl/
+co.pl
+
+// Microsoft : http://microsoft.com
+// Submitted by Barry Dorrans <bdorrans@microsoft.com> 2014-01-24
+azurewebsites.net
+azure-mobile.net
+cloudapp.net
+
+// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
+// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net> 2014-02-02
+nfshost.com
+
+// NYC.mn : http://www.information.nyc.mn
+// Submitted by Matthew Brown <mattbrown@nyc.mn> 2013-03-11
+nyc.mn
+
+// One Fold Media : http://www.onefoldmedia.com/
+// Submitted by Eddie Jones <eddie@onefoldmedia.com> 2014-06-10
+nid.io
+
+// Opera Software, A.S.A.
+// Submitted by Yngve Pettersen <yngve@opera.com> 2009-11-26
+operaunite.com
+
+// OutSystems
+// Submitted by Duarte Santos <domain-admin@outsystemscloud.com> 2014-03-11
+outsystemscloud.com
+
+// .pl domains (grandfathered)
+art.pl
+gliwice.pl
+krakow.pl
+poznan.pl
+wroc.pl
+zakopane.pl
+
+// priv.at : http://www.nic.priv.at/
+// Submitted by registry <lendl@nic.at> 2008-06-09
+priv.at
+
+// Red Hat, Inc. OpenShift : https://openshift.redhat.com/
+// Submitted by Tim Kramer <tkramer@rhcloud.com> 2012-10-24
+rhcloud.com
+
+// SinaAppEngine : http://sae.sina.com.cn/
+// Submitted by SinaAppEngine <saesupport@sinacloud.com> 2015-02-02
+sinaapp.com
+vipsinaapp.com
+1kapp.com
+
+// TASK geographical domains (www.task.gda.pl/uslugi/dns)
+gda.pl
+gdansk.pl
+gdynia.pl
+med.pl
+sopot.pl
+
+// UDR Limited : http://www.udr.hk.com
+// Submitted by registry <hostmaster@udr.hk.com> 2014-11-07
+hk.com
+hk.org
+ltd.hk
+inc.hk
+
+// Yola : https://www.yola.com/
+// Submitted by Stefano Rivera <stefano@yola.com> 2014-07-09
+yolasite.com
+
+// ZaNiC : http://www.za.net/
+// Submitted by registry <hostmaster@nic.za.net> 2009-10-03
+za.net
+za.org
+
+// ===END PRIVATE DOMAINS===
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var async = require('async');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+var CookieJar = tough.CookieJar;
+
+
+var atNow = Date.now();
+
+function at(offset) {
+ return {now: new Date(atNow + offset)};
+}
+
+vows
+ .describe('API')
+ .addBatch({
+ "All defined": function () {
+ assert.ok(Cookie);
+ assert.ok(CookieJar);
+ }
+ })
+ .addBatch({
+ "Constructor": {
+ topic: function () {
+ return new Cookie({
+ key: 'test',
+ value: 'b',
+ maxAge: 60
+ });
+ },
+ 'check for key property': function (c) {
+ assert.ok(c);
+ assert.equal(c.key, 'test');
+ },
+ 'check for value property': function (c) {
+ assert.equal(c.value, 'b');
+ },
+ 'check for maxAge': function (c) {
+ assert.equal(c.maxAge, 60);
+ },
+ 'check for default values for unspecified properties': function (c) {
+ assert.equal(c.expires, "Infinity");
+ assert.equal(c.secure, false);
+ assert.equal(c.httpOnly, false);
+ }
+ }
+ })
+ .addBatch({
+ "expiry option": {
+ topic: function () {
+ var cb = this.callback;
+ var cj = new CookieJar();
+ cj.setCookie('near=expiry; Domain=example.com; Path=/; Max-Age=1', 'http://www.example.com', at(-1), function (err, cookie) {
+
+ cb(err, {cj: cj, cookie: cookie});
+ });
+ },
+ "set the cookie": function (t) {
+ assert.ok(t.cookie, "didn't set?!");
+ assert.equal(t.cookie.key, 'near');
+ },
+ "then, retrieving": {
+ topic: function (t) {
+ var cb = this.callback;
+ setTimeout(function () {
+ t.cj.getCookies('http://www.example.com', {http: true, expire: false}, function (err, cookies) {
+ t.cookies = cookies;
+ cb(err, t);
+ });
+ }, 2000);
+ },
+ "got the cookie": function (t) {
+ assert.lengthOf(t.cookies, 1);
+ assert.equal(t.cookies[0].key, 'near');
+ }
+ }
+ }
+ })
+ .addBatch({
+ "allPaths option": {
+ topic: function () {
+ var cj = new CookieJar();
+ var tasks = [];
+ tasks.push(cj.setCookie.bind(cj, 'nopath_dom=qq; Path=/; Domain=example.com', 'http://example.com', {}));
+ tasks.push(cj.setCookie.bind(cj, 'path_dom=qq; Path=/foo; Domain=example.com', 'http://example.com', {}));
+ tasks.push(cj.setCookie.bind(cj, 'nopath_host=qq; Path=/', 'http://www.example.com', {}));
+ tasks.push(cj.setCookie.bind(cj, 'path_host=qq; Path=/foo', 'http://www.example.com', {}));
+ tasks.push(cj.setCookie.bind(cj, 'other=qq; Path=/', 'http://other.example.com/', {}));
+ tasks.push(cj.setCookie.bind(cj, 'other2=qq; Path=/foo', 'http://other.example.com/foo', {}));
+ var cb = this.callback;
+ async.parallel(tasks, function (err, results) {
+ cb(err, {cj: cj, cookies: results});
+ });
+ },
+ "all set": function (t) {
+ assert.equal(t.cookies.length, 6);
+ assert.ok(t.cookies.every(function (c) {
+ return !!c
+ }));
+ },
+ "getting without allPaths": {
+ topic: function (t) {
+ var cb = this.callback;
+ var cj = t.cj;
+ cj.getCookies('http://www.example.com/', {}, function (err, cookies) {
+ cb(err, {cj: cj, cookies: cookies});
+ });
+ },
+ "found just two cookies": function (t) {
+ assert.equal(t.cookies.length, 2);
+ },
+ "all are path=/": function (t) {
+ assert.ok(t.cookies.every(function (c) {
+ return c.path === '/'
+ }));
+ },
+ "no 'other' cookies": function (t) {
+ assert.ok(!t.cookies.some(function (c) {
+ return (/^other/).test(c.name)
+ }));
+ }
+ },
+ "getting without allPaths for /foo": {
+ topic: function (t) {
+ var cb = this.callback;
+ var cj = t.cj;
+ cj.getCookies('http://www.example.com/foo', {}, function (err, cookies) {
+ cb(err, {cj: cj, cookies: cookies});
+ });
+ },
+ "found four cookies": function (t) {
+ assert.equal(t.cookies.length, 4);
+ },
+ "no 'other' cookies": function (t) {
+ assert.ok(!t.cookies.some(function (c) {
+ return (/^other/).test(c.name)
+ }));
+ }
+ },
+ "getting with allPaths:true": {
+ topic: function (t) {
+ var cb = this.callback;
+ var cj = t.cj;
+ cj.getCookies('http://www.example.com/', {allPaths: true}, function (err, cookies) {
+ cb(err, {cj: cj, cookies: cookies});
+ });
+ },
+ "found four cookies": function (t) {
+ assert.equal(t.cookies.length, 4);
+ },
+ "no 'other' cookies": function (t) {
+ assert.ok(!t.cookies.some(function (c) {
+ return (/^other/).test(c.name)
+ }));
+ }
+ }
+ }
+ })
+ .addBatch({
+ "Remove cookies": {
+ topic: function () {
+ var jar = new CookieJar();
+ var cookie = Cookie.parse("a=b; Domain=example.com; Path=/");
+ var cookie2 = Cookie.parse("a=b; Domain=foo.com; Path=/");
+ var cookie3 = Cookie.parse("foo=bar; Domain=foo.com; Path=/");
+ jar.setCookie(cookie, 'http://example.com/index.html', function () {
+ });
+ jar.setCookie(cookie2, 'http://foo.com/index.html', function () {
+ });
+ jar.setCookie(cookie3, 'http://foo.com/index.html', function () {
+ });
+ return jar;
+ },
+ "all from matching domain": function (jar) {
+ jar.store.removeCookies('example.com', null, function (err) {
+ assert(err == null);
+
+ jar.store.findCookies('example.com', null, function (err, cookies) {
+ assert(err == null);
+ assert(cookies != null);
+ assert(cookies.length === 0, 'cookie was not removed');
+ });
+
+ jar.store.findCookies('foo.com', null, function (err, cookies) {
+ assert(err == null);
+ assert(cookies != null);
+ assert(cookies.length === 2, 'cookies should not have been removed');
+ });
+ });
+ },
+ "from cookie store matching domain and key": function (jar) {
+ jar.store.removeCookie('foo.com', '/', 'foo', function (err) {
+ assert(err == null);
+
+ jar.store.findCookies('foo.com', null, function (err, cookies) {
+ assert(err == null);
+ assert(cookies != null);
+ assert(cookies.length === 1, 'cookie was not removed correctly');
+ assert(cookies[0].key === 'a', 'wrong cookie was removed');
+ });
+ });
+ }
+ }
+ })
+ .addBatch({
+ "Synchronous CookieJar": {
+ "setCookieSync": {
+ topic: function () {
+ var jar = new CookieJar();
+ var cookie = Cookie.parse("a=b; Domain=example.com; Path=/");
+ cookie = jar.setCookieSync(cookie, 'http://example.com/index.html');
+ return cookie;
+ },
+ "returns a copy of the cookie": function (cookie) {
+ assert.instanceOf(cookie, Cookie);
+ }
+ },
+ "getCookiesSync": {
+ topic: function () {
+ var jar = new CookieJar();
+ var url = 'http://example.com/index.html';
+ jar.setCookieSync("a=b; Domain=example.com; Path=/", url);
+ jar.setCookieSync("c=d; Domain=example.com; Path=/", url);
+ return jar.getCookiesSync(url);
+ },
+ "returns the cookie array": function (err, cookies) {
+ assert.ok(!err);
+ assert.ok(Array.isArray(cookies));
+ assert.lengthOf(cookies, 2);
+ cookies.forEach(function (cookie) {
+ assert.instanceOf(cookie, Cookie);
+ });
+ }
+ },
+
+ "getCookieStringSync": {
+ topic: function () {
+ var jar = new CookieJar();
+ var url = 'http://example.com/index.html';
+ jar.setCookieSync("a=b; Domain=example.com; Path=/", url);
+ jar.setCookieSync("c=d; Domain=example.com; Path=/", url);
+ return jar.getCookieStringSync(url);
+ },
+ "returns the cookie header string": function (err, str) {
+ assert.ok(!err);
+ assert.typeOf(str, 'string');
+ }
+ },
+
+ "getSetCookieStringsSync": {
+ topic: function () {
+ var jar = new CookieJar();
+ var url = 'http://example.com/index.html';
+ jar.setCookieSync("a=b; Domain=example.com; Path=/", url);
+ jar.setCookieSync("c=d; Domain=example.com; Path=/", url);
+ return jar.getSetCookieStringsSync(url);
+ },
+ "returns the cookie header string": function (err, headers) {
+ assert.ok(!err);
+ assert.ok(Array.isArray(headers));
+ assert.lengthOf(headers, 2);
+ headers.forEach(function (header) {
+ assert.typeOf(header, 'string');
+ });
+ }
+ }
+ }
+ })
+ .addBatch({
+ "Synchronous API on async CookieJar": {
+ topic: function () {
+ return new tough.Store();
+ },
+ "setCookieSync": {
+ topic: function (store) {
+ var jar = new CookieJar(store);
+ try {
+ jar.setCookieSync("a=b", 'http://example.com/index.html');
+ return false;
+ } catch (e) {
+ return e;
+ }
+ },
+ "fails": function (err) {
+ assert.instanceOf(err, Error);
+ assert.equal(err.message,
+ 'CookieJar store is not synchronous; use async API instead.');
+ }
+ },
+ "getCookiesSync": {
+ topic: function (store) {
+ var jar = new CookieJar(store);
+ try {
+ jar.getCookiesSync('http://example.com/index.html');
+ return false;
+ } catch (e) {
+ return e;
+ }
+ },
+ "fails": function (err) {
+ assert.instanceOf(err, Error);
+ assert.equal(err.message,
+ 'CookieJar store is not synchronous; use async API instead.');
+ }
+ },
+ "getCookieStringSync": {
+ topic: function (store) {
+ var jar = new CookieJar(store);
+ try {
+ jar.getCookieStringSync('http://example.com/index.html');
+ return false;
+ } catch (e) {
+ return e;
+ }
+ },
+ "fails": function (err) {
+ assert.instanceOf(err, Error);
+ assert.equal(err.message,
+ 'CookieJar store is not synchronous; use async API instead.');
+ }
+ },
+ "getSetCookieStringsSync": {
+ topic: function (store) {
+ var jar = new CookieJar(store);
+ try {
+ jar.getSetCookieStringsSync('http://example.com/index.html');
+ return false;
+ } catch (e) {
+ return e;
+ }
+ },
+ "fails": function (err) {
+ assert.instanceOf(err, Error);
+ assert.equal(err.message,
+ 'CookieJar store is not synchronous; use async API instead.');
+ }
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var async = require('async');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+var CookieJar = tough.CookieJar;
+
+var atNow = Date.now();
+
+function at(offset) {
+ return {now: new Date(atNow + offset)};
+}
+
+vows
+ .describe('CookieJar')
+ .addBatch({
+ "Setting a basic cookie": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=example.com; Path=/");
+ assert.strictEqual(c.hostOnly, null);
+ assert.instanceOf(c.creation, Date);
+ assert.strictEqual(c.lastAccessed, null);
+ c.creation = new Date(Date.now() - 10000);
+ cj.setCookie(c, 'http://example.com/index.html', this.callback);
+ },
+ "works": function (c) {
+ assert.instanceOf(c, Cookie)
+ }, // C is for Cookie, good enough for me
+ "gets timestamped": function (c) {
+ assert.ok(c.creation);
+ assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
+ assert.ok(c.lastAccessed);
+ assert.equal(c.creation, c.lastAccessed);
+ assert.equal(c.TTL(), Infinity);
+ assert.ok(!c.isPersistent());
+ }
+ },
+ "Setting a no-path cookie": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=example.com");
+ assert.strictEqual(c.hostOnly, null);
+ assert.instanceOf(c.creation, Date);
+ assert.strictEqual(c.lastAccessed, null);
+ c.creation = new Date(Date.now() - 10000);
+ cj.setCookie(c, 'http://example.com/index.html', this.callback);
+ },
+ "domain": function (c) {
+ assert.equal(c.domain, 'example.com')
+ },
+ "path is /": function (c) {
+ assert.equal(c.path, '/')
+ },
+ "path was derived": function (c) {
+ assert.strictEqual(c.pathIsDefault, true)
+ }
+ },
+ "Setting a cookie already marked as host-only": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=example.com");
+ assert.strictEqual(c.hostOnly, null);
+ assert.instanceOf(c.creation, Date);
+ assert.strictEqual(c.lastAccessed, null);
+ c.creation = new Date(Date.now() - 10000);
+ c.hostOnly = true;
+ cj.setCookie(c, 'http://example.com/index.html', this.callback);
+ },
+ "domain": function (c) {
+ assert.equal(c.domain, 'example.com')
+ },
+ "still hostOnly": function (c) {
+ assert.strictEqual(c.hostOnly, true)
+ }
+ },
+ "Setting a session cookie": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b");
+ assert.strictEqual(c.path, null);
+ cj.setCookie(c, 'http://www.example.com/dir/index.html', this.callback);
+ },
+ "works": function (c) {
+ assert.instanceOf(c, Cookie)
+ },
+ "gets the domain": function (c) {
+ assert.equal(c.domain, 'www.example.com')
+ },
+ "gets the default path": function (c) {
+ assert.equal(c.path, '/dir')
+ },
+ "is 'hostOnly'": function (c) {
+ assert.ok(c.hostOnly)
+ }
+ },
+ "Setting wrong domain cookie": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=fooxample.com; Path=/");
+ cj.setCookie(c, 'http://example.com/index.html', this.callback);
+ },
+ "fails": function (err, c) {
+ assert.ok(err.message.match(/domain/i));
+ assert.ok(!c);
+ }
+ },
+ "Setting sub-domain cookie": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=www.example.com; Path=/");
+ cj.setCookie(c, 'http://example.com/index.html', this.callback);
+ },
+ "fails": function (err, c) {
+ assert.ok(err.message.match(/domain/i));
+ assert.ok(!c);
+ }
+ },
+ "Setting super-domain cookie": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=example.com; Path=/");
+ cj.setCookie(c, 'http://www.app.example.com/index.html', this.callback);
+ },
+ "success": function (err, c) {
+ assert.ok(!err);
+ assert.equal(c.domain, 'example.com');
+ }
+ },
+ "Setting a sub-path cookie on a super-domain": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=example.com; Path=/subpath");
+ assert.strictEqual(c.hostOnly, null);
+ assert.instanceOf(c.creation, Date);
+ assert.strictEqual(c.lastAccessed, null);
+ c.creation = new Date(Date.now() - 10000);
+ cj.setCookie(c, 'http://www.example.com/index.html', this.callback);
+ },
+ "domain is super-domain": function (c) {
+ assert.equal(c.domain, 'example.com')
+ },
+ "path is /subpath": function (c) {
+ assert.equal(c.path, '/subpath')
+ },
+ "path was NOT derived": function (c) {
+ assert.strictEqual(c.pathIsDefault, null)
+ }
+ },
+ "Setting HttpOnly cookie over non-HTTP API": {
+ topic: function () {
+ var cj = new CookieJar();
+ var c = Cookie.parse("a=b; Domain=example.com; Path=/; HttpOnly");
+ cj.setCookie(c, 'http://example.com/index.html', {http: false}, this.callback);
+ },
+ "fails": function (err, c) {
+ assert.match(err.message, /HttpOnly/i);
+ assert.ok(!c);
+ }
+ }
+ })
+ .addBatch({
+ "Store eight cookies": {
+ topic: function () {
+ var cj = new CookieJar();
+ var ex = 'http://example.com/index.html';
+ var tasks = [];
+ tasks.push(function (next) {
+ cj.setCookie('a=1; Domain=example.com; Path=/', ex, at(0), next);
+ });
+ tasks.push(function (next) {
+ cj.setCookie('b=2; Domain=example.com; Path=/; HttpOnly', ex, at(1000), next);
+ });
+ tasks.push(function (next) {
+ cj.setCookie('c=3; Domain=example.com; Path=/; Secure', ex, at(2000), next);
+ });
+ tasks.push(function (next) { // path
+ cj.setCookie('d=4; Domain=example.com; Path=/foo', ex, at(3000), next);
+ });
+ tasks.push(function (next) { // host only
+ cj.setCookie('e=5', ex, at(4000), next);
+ });
+ tasks.push(function (next) { // other domain
+ cj.setCookie('f=6; Domain=nodejs.org; Path=/', 'http://nodejs.org', at(5000), next);
+ });
+ tasks.push(function (next) { // expired
+ cj.setCookie('g=7; Domain=example.com; Path=/; Expires=Tue, 18 Oct 2011 00:00:00 GMT', ex, at(6000), next);
+ });
+ tasks.push(function (next) { // expired via Max-Age
+ cj.setCookie('h=8; Domain=example.com; Path=/; Max-Age=1', ex, next);
+ });
+ var cb = this.callback;
+ async.parallel(tasks, function (err, results) {
+ setTimeout(function () {
+ cb(err, cj, results);
+ }, 2000); // so that 'h=8' expires
+ });
+ },
+ "setup ok": function (err, cj, results) {
+ assert.ok(!err);
+ assert.ok(cj);
+ assert.ok(results);
+ },
+ "then retrieving for http://nodejs.org": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('http://nodejs.org', this.callback);
+ },
+ "get a nodejs cookie": function (cookies) {
+ assert.lengthOf(cookies, 1);
+ var cookie = cookies[0];
+ assert.equal(cookie.domain, 'nodejs.org');
+ }
+ },
+ "then retrieving for https://example.com": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('https://example.com', {secure: true}, this.callback);
+ },
+ "get a secure example cookie with others": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['a', 'b', 'c', 'e']);
+ }
+ },
+ "then retrieving for https://example.com (missing options)": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('https://example.com', this.callback);
+ },
+ "get a secure example cookie with others": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['a', 'b', 'c', 'e']);
+ }
+ },
+ "then retrieving for http://example.com": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('http://example.com', this.callback);
+ },
+ "get a bunch of cookies": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['a', 'b', 'e']);
+ }
+ },
+ "then retrieving for http://EXAMPlE.com": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('http://EXAMPlE.com', this.callback);
+ },
+ "get a bunch of cookies": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['a', 'b', 'e']);
+ }
+ },
+ "then retrieving for http://example.com, non-HTTP": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('http://example.com', {http: false}, this.callback);
+ },
+ "get a bunch of cookies": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['a', 'e']);
+ }
+ },
+ "then retrieving for http://example.com/foo/bar": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('http://example.com/foo/bar', this.callback);
+ },
+ "get a bunch of cookies": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['d', 'a', 'b', 'e']);
+ }
+ },
+ "then retrieving for http://example.com as a string": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookieString('http://example.com', this.callback);
+ },
+ "get a single string": function (cookieHeader) {
+ assert.equal(cookieHeader, "a=1; b=2; e=5");
+ }
+ },
+ "then retrieving for http://example.com as a set-cookie header": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getSetCookieStrings('http://example.com', this.callback);
+ },
+ "get a single string": function (cookieHeaders) {
+ assert.lengthOf(cookieHeaders, 3);
+ assert.equal(cookieHeaders[0], "a=1; Domain=example.com; Path=/");
+ assert.equal(cookieHeaders[1], "b=2; Domain=example.com; Path=/; HttpOnly");
+ assert.equal(cookieHeaders[2], "e=5; Path=/");
+ }
+ },
+ "then retrieving for http://www.example.com/": {
+ topic: function (cj, oldResults) {
+ assert.ok(oldResults);
+ cj.getCookies('http://www.example.com/foo/bar', this.callback);
+ },
+ "get a bunch of cookies": function (cookies) {
+ var names = cookies.map(function (c) {
+ return c.key
+ });
+ assert.deepEqual(names, ['d', 'a', 'b']); // note lack of 'e'
+ }
+ }
+ }
+ })
+ .addBatch({
+ "Repeated names": {
+ topic: function () {
+ var cb = this.callback;
+ var cj = new CookieJar();
+ var ex = 'http://www.example.com/';
+ var sc = cj.setCookie;
+ var tasks = [];
+ var now = Date.now();
+ tasks.push(sc.bind(cj, 'aaaa=xxxx', ex, at(0)));
+ tasks.push(sc.bind(cj, 'aaaa=1111; Domain=www.example.com', ex, at(1000)));
+ tasks.push(sc.bind(cj, 'aaaa=2222; Domain=example.com', ex, at(2000)));
+ tasks.push(sc.bind(cj, 'aaaa=3333; Domain=www.example.com; Path=/pathA', ex, at(3000)));
+ async.series(tasks, function (err, results) {
+ results = results.filter(function (e) {
+ return e !== undefined
+ });
+ cb(err, {cj: cj, cookies: results, now: now});
+ });
+ },
+ "all got set": function (err, t) {
+ assert.lengthOf(t.cookies, 4);
+ },
+ "then getting 'em back": {
+ topic: function (t) {
+ var cj = t.cj;
+ cj.getCookies('http://www.example.com/pathA', this.callback);
+ },
+ "there's just three": function (err, cookies) {
+ var vals = cookies.map(function (c) {
+ return c.value
+ });
+ // may break with sorting; sorting should put 3333 first due to longest path:
+ assert.deepEqual(vals, ['3333', '1111', '2222']);
+ }
+ }
+ }
+ })
+ .addBatch({
+ "CookieJar setCookie errors": {
+ "public-suffix domain": {
+ topic: function () {
+ var cj = new CookieJar();
+ cj.setCookie('i=9; Domain=kyoto.jp; Path=/', 'kyoto.jp', this.callback);
+ },
+ "errors": function (err, cookie) {
+ assert.ok(err);
+ assert.ok(!cookie);
+ assert.match(err.message, /public suffix/i);
+ }
+ },
+ "wrong domain": {
+ topic: function () {
+ var cj = new CookieJar();
+ cj.setCookie('j=10; Domain=google.com; Path=/', 'http://google.ca', this.callback);
+ },
+ "errors": function (err, cookie) {
+ assert.ok(err);
+ assert.ok(!cookie);
+ assert.match(err.message, /not in this host's domain/i);
+ }
+ },
+ "old cookie is HttpOnly": {
+ topic: function () {
+ var cb = this.callback;
+ var next = function (err, c) {
+ c = null;
+ return cb(err, cj);
+ };
+ var cj = new CookieJar();
+ cj.setCookie('k=11; Domain=example.ca; Path=/; HttpOnly', 'http://example.ca', {http: true}, next);
+ },
+ "initial cookie is set": function (err, cj) {
+ assert.ok(!err);
+ assert.ok(cj);
+ },
+ "but when trying to overwrite": {
+ topic: function (cj) {
+ var cb = this.callback;
+ var next = function (err, c) {
+ c = null;
+ cb(null, err);
+ };
+ cj.setCookie('k=12; Domain=example.ca; Path=/', 'http://example.ca', {http: false}, next);
+ },
+ "it's an error": function (err) {
+ assert.ok(err);
+ },
+ "then, checking the original": {
+ topic: function (ignored, cj) {
+ assert.ok(cj instanceof CookieJar);
+ cj.getCookies('http://example.ca', {http: true}, this.callback);
+ },
+ "cookie has original value": function (err, cookies) {
+ assert.equal(err, null);
+ assert.lengthOf(cookies, 1);
+ assert.equal(cookies[0].value, 11);
+ }
+ }
+ }
+ },
+ "similar to public suffix": {
+ topic: function () {
+ var cj = new CookieJar();
+ var url = 'http://www.foonet.net';
+ assert.isTrue(cj.rejectPublicSuffixes);
+ cj.setCookie('l=13; Domain=foonet.net; Path=/', url, this.callback);
+ },
+ "doesn't error": function (err, cookie) {
+ assert.ok(!err);
+ assert.ok(cookie);
+ }
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+
+function toKeyArray(cookies) {
+ return cookies.map(function (c) {
+ return c.key
+ });
+}
+
+vows
+ .describe('Cookie sorting')
+ .addBatch({
+ "Cookie Sorting": {
+ topic: function () {
+ var cookies = [];
+ cookies.push(Cookie.parse("a=0; Domain=example.com"));
+ cookies.push(Cookie.parse("b=1; Domain=www.example.com"));
+ cookies.push(Cookie.parse("c=2; Domain=example.com; Path=/pathA"));
+ cookies.push(Cookie.parse("d=3; Domain=www.example.com; Path=/pathA"));
+ cookies.push(Cookie.parse("e=4; Domain=example.com; Path=/pathA/pathB"));
+ cookies.push(Cookie.parse("f=5; Domain=www.example.com; Path=/pathA/pathB"));
+
+ // weak shuffle:
+ cookies = cookies.sort(function () {
+ return Math.random() - 0.5
+ });
+
+ cookies = cookies.sort(tough.cookieCompare);
+ return cookies;
+ },
+ "got": function (cookies) {
+ assert.lengthOf(cookies, 6);
+ assert.deepEqual(toKeyArray(cookies), ['e', 'f', 'c', 'd', 'a', 'b']);
+ }
+ }
+ })
+ .addBatch({
+ "Changing creation date affects sorting": {
+ topic: function () {
+ var cookies = [];
+ var now = Date.now();
+ cookies.push(Cookie.parse("a=0;"));
+ cookies.push(Cookie.parse("b=1;"));
+ cookies.push(Cookie.parse("c=2;"));
+
+ cookies.forEach(function (cookie, idx) {
+ cookie.creation = new Date(now - 100 * idx);
+ });
+
+ return cookies.sort(tough.cookieCompare);
+ },
+ "got": function (cookies) {
+ assert.deepEqual(toKeyArray(cookies), ['c', 'b', 'a']);
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+
+vows
+ .describe('Cookie.toJSON()')
+ .addBatch({
+ "JSON": {
+ "serialization": {
+ topic: function() {
+ var c = Cookie.parse('alpha=beta; Domain=example.com; Path=/foo; Expires=Tue, 19 Jan 2038 03:14:07 GMT; HttpOnly');
+ return JSON.stringify(c);
+ },
+ "gives a string": function(str) {
+ assert.equal(typeof str, "string");
+ },
+ "date is in ISO format": function(str) {
+ assert.match(str, /"expires":"2038-01-19T03:14:07\.000Z"/, 'expires is in ISO format');
+ }
+ },
+ "deserialization": {
+ topic: function() {
+ var json = '{"key":"alpha","value":"beta","domain":"example.com","path":"/foo","expires":"2038-01-19T03:14:07.000Z","httpOnly":true,"lastAccessed":2000000000123}';
+ return Cookie.fromJSON(json);
+ },
+ "works": function(c) {
+ assert.ok(c);
+ },
+ "key": function(c) { assert.equal(c.key, "alpha") },
+ "value": function(c) { assert.equal(c.value, "beta") },
+ "domain": function(c) { assert.equal(c.domain, "example.com") },
+ "path": function(c) { assert.equal(c.path, "/foo") },
+ "httpOnly": function(c) { assert.strictEqual(c.httpOnly, true) },
+ "secure": function(c) { assert.strictEqual(c.secure, false) },
+ "hostOnly": function(c) { assert.strictEqual(c.hostOnly, null) },
+ "expires is a date object": function(c) {
+ assert.equal(c.expires.getTime(), 2147483647000);
+ },
+ "lastAccessed is a date object": function(c) {
+ assert.equal(c.lastAccessed.getTime(), 2000000000123);
+ },
+ "creation defaulted": function(c) {
+ assert.ok(c.creation.getTime());
+ }
+ },
+ "null deserialization": {
+ topic: function() {
+ return Cookie.fromJSON(null);
+ },
+ "is null": function(cookie) {
+ assert.equal(cookie,null);
+ }
+ }
+ },
+ "expiry deserialization": {
+ "Infinity": {
+ topic: Cookie.fromJSON.bind(null, '{"expires":"Infinity"}'),
+ "is infinite": function(c) {
+ assert.strictEqual(c.expires, "Infinity");
+ assert.equal(c.expires, Infinity);
+ }
+ }
+ },
+ "maxAge serialization": {
+ topic: function() {
+ return function(toSet) {
+ var c = new Cookie();
+ c.key = 'foo'; c.value = 'bar';
+ c.setMaxAge(toSet);
+ return JSON.stringify(c);
+ };
+ },
+ "zero": {
+ topic: function(f) { return f(0) },
+ "looks good": function(str) {
+ assert.match(str, /"maxAge":0/);
+ }
+ },
+ "Infinity": {
+ topic: function(f) { return f(Infinity) },
+ "looks good": function(str) {
+ assert.match(str, /"maxAge":"Infinity"/);
+ }
+ },
+ "-Infinity": {
+ topic: function(f) { return f(-Infinity) },
+ "looks good": function(str) {
+ assert.match(str, /"maxAge":"-Infinity"/);
+ }
+ },
+ "null": {
+ topic: function(f) { return f(null) },
+ "looks good": function(str) {
+ assert.match(str, /"maxAge":null/);
+ }
+ }
+ },
+ "maxAge deserialization": {
+ "number": {
+ topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":123}'),
+ "is the number": function(c) {
+ assert.strictEqual(c.maxAge, 123);
+ }
+ },
+ "null": {
+ topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":null}'),
+ "is null": function(c) {
+ assert.strictEqual(c.maxAge, null);
+ }
+ },
+ "less than zero": {
+ topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":-123}'),
+ "is -123": function(c) {
+ assert.strictEqual(c.maxAge, -123);
+ }
+ },
+ "Infinity": {
+ topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"Infinity"}'),
+ "is inf-as-string": function(c) {
+ assert.strictEqual(c.maxAge, "Infinity");
+ }
+ },
+ "-Infinity": {
+ topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"-Infinity"}'),
+ "is inf-as-string": function(c) {
+ assert.strictEqual(c.maxAge, "-Infinity");
+ }
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+
+vows
+ .describe('Cookie.toString()')
+ .addBatch({
+ "a simple cookie": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ return c;
+ },
+ "validates": function (c) {
+ assert.ok(c.validate());
+ },
+ "to string": function (c) {
+ assert.equal(c.toString(), 'a=b');
+ }
+ },
+ "a cookie with spaces in the value": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'beta gamma';
+ return c;
+ },
+ "doesn't validate": function (c) {
+ assert.ok(!c.validate());
+ },
+ "'garbage in, garbage out'": function (c) {
+ assert.equal(c.toString(), 'a=beta gamma');
+ }
+ },
+ "with an empty value and HttpOnly": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.httpOnly = true;
+ return c;
+ },
+ "to string": function (c) {
+ assert.equal(c.toString(), 'a=; HttpOnly');
+ }
+ },
+ "with an expiry": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.setExpires("Oct 18 2011 07:05:03 GMT");
+ return c;
+ },
+ "validates": function (c) {
+ assert.ok(c.validate());
+ },
+ "to string": function (c) {
+ assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT');
+ },
+ "to short string": function (c) {
+ assert.equal(c.cookieString(), 'a=b');
+ }
+ },
+ "with a max-age": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.setExpires("Oct 18 2011 07:05:03 GMT");
+ c.maxAge = 12345;
+ return c;
+ },
+ "validates": function (c) {
+ assert.ok(c.validate()); // mabe this one *shouldn't*?
+ },
+ "to string": function (c) {
+ assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345');
+ }
+ },
+ "with a bunch of things": function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.setExpires("Oct 18 2011 07:05:03 GMT");
+ c.maxAge = 12345;
+ c.domain = 'example.com';
+ c.path = '/foo';
+ c.secure = true;
+ c.httpOnly = true;
+ c.extensions = ['MyExtension'];
+ assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345; Domain=example.com; Path=/foo; Secure; HttpOnly; MyExtension');
+ },
+ "a host-only cookie": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.hostOnly = true;
+ c.domain = 'shouldnt-stringify.example.com';
+ c.path = '/should-stringify';
+ return c;
+ },
+ "validates": function (c) {
+ assert.ok(c.validate());
+ },
+ "to string": function (c) {
+ assert.equal(c.toString(), 'a=b; Path=/should-stringify');
+ }
+ },
+ "minutes are '10'": {
+ topic: function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.expires = new Date(1284113410000);
+ return c;
+ },
+ "validates": function (c) {
+ assert.ok(c.validate());
+ },
+ "to string": function (c) {
+ var str = c.toString();
+ assert.notEqual(str, 'a=b; Expires=Fri, 010 Sep 2010 010:010:010 GMT');
+ assert.equal(str, 'a=b; Expires=Fri, 10 Sep 2010 10:10:10 GMT');
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+
+function dateVows(table) {
+ var theVows = {};
+ Object.keys(table).forEach(function (date) {
+ var expect = table[date];
+ theVows[date] = function () {
+ var got = tough.parseDate(date) ? 'valid' : 'invalid';
+ assert.equal(got, expect ? 'valid' : 'invalid');
+ };
+ });
+ return {"date parsing": theVows};
+}
+
+vows
+ .describe('Date')
+ .addBatch(dateVows({
+ "Wed, 09 Jun 2021 10:18:14 GMT": true,
+ "Wed, 09 Jun 2021 22:18:14 GMT": true,
+ "Tue, 18 Oct 2011 07:42:42.123 GMT": true,
+ "18 Oct 2011 07:42:42 GMT": true,
+ "8 Oct 2011 7:42:42 GMT": true,
+ "8 Oct 2011 7:2:42 GMT": true,
+ "Oct 18 2011 07:42:42 GMT": true,
+ "Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": true,
+ "09 Jun 2021 10:18:14 GMT": true,
+ "99 Jix 3038 48:86:72 ZMT": false,
+ '01 Jan 1970 00:00:00 GMT': true,
+ '01 Jan 1600 00:00:00 GMT': false, // before 1601
+ '01 Jan 1601 00:00:00 GMT': true,
+ '10 Feb 81 13:00:00 GMT': true, // implicit year
+ 'Thu, 17-Apr-2014 02:12:29 GMT': true, // dashes
+ 'Thu, 17-Apr-2014 02:12:29 UTC': true // dashes and UTC
+ }))
+ .addBatch({
+ "strict date parse of Thu, 01 Jan 1970 00:00:010 GMT": {
+ topic: function () {
+ return tough.parseDate('Thu, 01 Jan 1970 00:00:010 GMT', true) ? true : false;
+ },
+ "invalid": function (date) {
+ assert.equal(date, false);
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+
+function matchVows(func, table) {
+ var theVows = {};
+ table.forEach(function (item) {
+ var str = item[0];
+ var dom = item[1];
+ var expect = item[2];
+ var label = str + (expect ? " matches " : " doesn't match ") + dom;
+ theVows[label] = function () {
+ assert.equal(func(str, dom), expect);
+ };
+ });
+ return theVows;
+}
+
+function defaultPathVows(table) {
+ var theVows = {};
+ table.forEach(function (item) {
+ var str = item[0];
+ var expect = item[1];
+ var label = str + " gives " + expect;
+ theVows[label] = function () {
+ assert.equal(tough.defaultPath(str), expect);
+ };
+ });
+ return theVows;
+}
+
+vows
+ .describe('Domain and Path')
+ .addBatch({
+ "domain normalization": {
+ "simple": function () {
+ var c = new Cookie();
+ c.domain = "EXAMPLE.com";
+ assert.equal(c.canonicalizedDomain(), "example.com");
+ },
+ "extra dots": function () {
+ var c = new Cookie();
+ c.domain = ".EXAMPLE.com";
+ assert.equal(c.cdomain(), "example.com");
+ },
+ "weird trailing dot": function () {
+ var c = new Cookie();
+ c.domain = "EXAMPLE.ca.";
+ assert.equal(c.canonicalizedDomain(), "example.ca.");
+ },
+ "weird internal dots": function () {
+ var c = new Cookie();
+ c.domain = "EXAMPLE...ca.";
+ assert.equal(c.canonicalizedDomain(), "example...ca.");
+ },
+ "IDN": function () {
+ var c = new Cookie();
+ c.domain = "δοκιμή.δοκιμή"; // "test.test" in greek
+ assert.equal(c.canonicalizedDomain(), "xn--jxalpdlp.xn--jxalpdlp");
+ }
+ }
+ })
+ .addBatch({
+ "Domain Match": matchVows(tough.domainMatch, [
+ // str, dom, expect
+ ["example.com", "example.com", true],
+ ["eXaMpLe.cOm", "ExAmPlE.CoM", true],
+ ["no.ca", "yes.ca", false],
+ ["wwwexample.com", "example.com", false],
+ ["www.example.com", "example.com", true],
+ ["example.com", "www.example.com", false],
+ ["www.subdom.example.com", "example.com", true],
+ ["www.subdom.example.com", "subdom.example.com", true],
+ ["example.com", "example.com.", false], // RFC6265 S4.1.2.3
+ ["192.168.0.1", "168.0.1", false], // S5.1.3 "The string is a host name"
+ [null, "example.com", null],
+ ["example.com", null, null],
+ [null, null, null],
+ [undefined, undefined, null],
+ ])
+ })
+
+ .addBatch({
+ "default-path": defaultPathVows([
+ [null, "/"],
+ ["/", "/"],
+ ["/file", "/"],
+ ["/dir/file", "/dir"],
+ ["noslash", "/"],
+ ])
+ })
+ .addBatch({
+ "Path-Match": matchVows(tough.pathMatch, [
+ // request, cookie, match
+ ["/", "/", true],
+ ["/dir", "/", true],
+ ["/", "/dir", false],
+ ["/dir/", "/dir/", true],
+ ["/dir/file", "/dir/", true],
+ ["/dir/file", "/dir", true],
+ ["/directory", "/dir", false],
+ ])
+ })
+ .addBatch({
+ "permuteDomain": {
+ "base case": {
+ topic: tough.permuteDomain.bind(null, 'example.com'),
+ "got the domain": function (list) {
+ assert.deepEqual(list, ['example.com']);
+ }
+ },
+ "two levels": {
+ topic: tough.permuteDomain.bind(null, 'foo.bar.example.com'),
+ "got three things": function (list) {
+ assert.deepEqual(list, ['example.com', 'bar.example.com', 'foo.bar.example.com']);
+ }
+ },
+ "local domain": {
+ topic: tough.permuteDomain.bind(null, 'foo.bar.example.localduhmain'),
+ "got three things": function (list) {
+ assert.deepEqual(list, ['example.localduhmain', 'bar.example.localduhmain', 'foo.bar.example.localduhmain']);
+ }
+ }
+ },
+ "permutePath": {
+ "base case": {
+ topic: tough.permutePath.bind(null, '/'),
+ "just slash": function (list) {
+ assert.deepEqual(list, ['/']);
+ }
+ },
+ "single case": {
+ topic: tough.permutePath.bind(null, '/foo'),
+ "two things": function (list) {
+ assert.deepEqual(list, ['/foo', '/']);
+ },
+ "path matching": function (list) {
+ list.forEach(function (e) {
+ assert.ok(tough.pathMatch('/foo', e));
+ });
+ }
+ },
+ "double case": {
+ topic: tough.permutePath.bind(null, '/foo/bar'),
+ "four things": function (list) {
+ assert.deepEqual(list, ['/foo/bar', '/foo', '/']);
+ },
+ "path matching": function (list) {
+ list.forEach(function (e) {
+ assert.ok(tough.pathMatch('/foo/bar', e));
+ });
+ }
+ },
+ "trailing slash": {
+ topic: tough.permutePath.bind(null, '/foo/bar/'),
+ "three things": function (list) {
+ assert.deepEqual(list, ['/foo/bar', '/foo', '/']);
+ },
+ "path matching": function (list) {
+ list.forEach(function (e) {
+ assert.ok(tough.pathMatch('/foo/bar/', e));
+ });
+ }
+ }
+ }
+ })
+ .export(module);
+
--- /dev/null
+[
+ {
+ "test": "Sat, 15-Apr-17 21:01:22 GMT",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Thu, 19-Apr-2007 16:00:00 GMT",
+ "expected": "Thu, 19 Apr 2007 16:00:00 GMT"
+ }, {
+ "test": "Wed, 25 Apr 2007 21:02:13 GMT",
+ "expected": "Wed, 25 Apr 2007 21:02:13 GMT"
+ }, {
+ "test": "Thu, 19/Apr\\2007 16:00:00 GMT",
+ "expected": "Thu, 19 Apr 2007 16:00:00 GMT"
+ }, {
+ "test": "Fri, 1 Jan 2010 01:01:50 GMT",
+ "expected": "Fri, 01 Jan 2010 01:01:50 GMT"
+ }, {
+ "test": "Wednesday, 1-Jan-2003 00:00:00 GMT",
+ "expected": "Wed, 01 Jan 2003 00:00:00 GMT"
+ }, {
+ "test": ", 1-Jan-2003 00:00:00 GMT",
+ "expected": "Wed, 01 Jan 2003 00:00:00 GMT"
+ }, {
+ "test": " 1-Jan-2003 00:00:00 GMT",
+ "expected": "Wed, 01 Jan 2003 00:00:00 GMT"
+ }, {
+ "test": "1-Jan-2003 00:00:00 GMT",
+ "expected": "Wed, 01 Jan 2003 00:00:00 GMT"
+ }, {
+ "test": "Wed,18-Apr-07 22:50:12 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "WillyWonka , 18-Apr-07 22:50:12 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "WillyWonka , 18-Apr-07 22:50:12",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "WillyWonka , 18-apr-07 22:50:12",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Mon, 18-Apr-1977 22:50:13 GMT",
+ "expected": "Mon, 18 Apr 1977 22:50:13 GMT"
+ }, {
+ "test": "Mon, 18-Apr-77 22:50:13 GMT",
+ "expected": "Mon, 18 Apr 1977 22:50:13 GMT"
+ }, {
+ "test": "\"Sat, 15-Apr-17\\\"21:01:22\\\"GMT\"",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Partyday, 18- April-07 22:50:12",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Partyday, 18 - Apri-07 22:50:12",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Wednes, 1-Januar-2003 00:00:00 GMT",
+ "expected": "Wed, 01 Jan 2003 00:00:00 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 GMT-2",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 GMT BLAH",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 GMT-0400",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 GMT-0400 (EDT)",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 DST",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 -0400",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 (hello there)",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 11:22:33",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 ::00 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 boink:z 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 91:22:33 21:01:22",
+ "expected": null
+ }, {
+ "test": "Thu Apr 18 22:50:12 2007 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "22:50:12 Thu Apr 18 2007 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Thu 22:50:12 Apr 18 2007 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Thu Apr 22:50:12 18 2007 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Thu Apr 18 22:50:12 2007 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Thu Apr 18 2007 22:50:12 GMT",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Thu Apr 18 2007 GMT 22:50:12",
+ "expected": "Wed, 18 Apr 2007 22:50:12 GMT"
+ }, {
+ "test": "Sat, 15-Apr-17 21:01:22 GMT",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "15-Sat, Apr-17 21:01:22 GMT",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "15-Sat, Apr 21:01:22 GMT 17",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "15-Sat, Apr 21:01:22 GMT 2017",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "15 Apr 21:01:22 2017",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "15 17 Apr 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Apr 15 17 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "Apr 15 21:01:22 17",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "2017 April 15 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "15 April 2017 21:01:22",
+ "expected": "Sat, 15 Apr 2017 21:01:22 GMT"
+ }, {
+ "test": "98 April 17 21:01:22",
+ "expected": null
+ }, {
+ "test": "Thu, 012-Aug-2008 20:49:07 GMT",
+ "expected": null
+ }, {
+ "test": "Thu, 12-Aug-31841 20:49:07 GMT",
+ "expected": null
+ }, {
+ "test": "Thu, 12-Aug-9999999999 20:49:07 GMT",
+ "expected": null
+ }, {
+ "test": "Thu, 999999999999-Aug-2007 20:49:07 GMT",
+ "expected": null
+ }, {
+ "test": "Thu, 12-Aug-2007 20:61:99999999999 GMT",
+ "expected": null
+ }, {
+ "test": "IAintNoDateFool",
+ "expected": null
+ }
+]
--- /dev/null
+[
+ {
+ "test": "Mon, 10-Dec-2007 17:02:24 GMT",
+ "expected": "Mon, 10 Dec 2007 17:02:24 GMT"
+ }, {
+ "test": "Wed, 09 Dec 2009 16:27:23 GMT",
+ "expected": "Wed, 09 Dec 2009 16:27:23 GMT"
+ }, {
+ "test": "Thursday, 01-Jan-1970 00:00:00 GMT",
+ "expected": "Thu, 01 Jan 1970 00:00:00 GMT"
+ }, {
+ "test": "Mon Dec 10 16:32:30 2007 GMT",
+ "expected": "Mon, 10 Dec 2007 16:32:30 GMT"
+ }, {
+ "test": "Wednesday, 01-Jan-10 00:00:00 GMT",
+ "expected": "Fri, 01 Jan 2010 00:00:00 GMT"
+ }, {
+ "test": "Mon, 10-Dec-07 20:35:03 GMT",
+ "expected": "Mon, 10 Dec 2007 20:35:03 GMT"
+ }, {
+ "test": "Wed, 1 Jan 2020 00:00:00 GMT",
+ "expected": "Wed, 01 Jan 2020 00:00:00 GMT"
+ }, {
+ "test": "Saturday, 8-Dec-2012 21:24:09 GMT",
+ "expected": "Sat, 08 Dec 2012 21:24:09 GMT"
+ }, {
+ "test": "Thu, 31 Dec 23:55:55 2037 GMT",
+ "expected": "Thu, 31 Dec 2037 23:55:55 GMT"
+ }, {
+ "test": "Sun, 9 Dec 2012 13:42:05 GMT",
+ "expected": "Sun, 09 Dec 2012 13:42:05 GMT"
+ }, {
+ "test": "Wed Dec 12 2007 08:44:07 GMT-0500 (EST)",
+ "expected": "Wed, 12 Dec 2007 08:44:07 GMT"
+ }, {
+ "test": "Mon, 01-Jan-2011 00: 00:00 GMT",
+ "expected": null
+ }, {
+ "test": "Sun, 1-Jan-1995 00:00:00 GMT",
+ "expected": "Sun, 01 Jan 1995 00:00:00 GMT"
+ }, {
+ "test": "Wednesday, 01-Jan-10 0:0:00 GMT",
+ "expected": "Fri, 01 Jan 2010 00:00:00 GMT"
+ }, {
+ "test": "Thu, 10 Dec 2009 13:57:2 GMT",
+ "expected": "Thu, 10 Dec 2009 13:57:02 GMT"
+ }
+]
--- /dev/null
+[
+ {
+ "test": "0001",
+ "received": [
+ "foo=bar"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0002",
+ "received": [
+ "foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0003",
+ "received": [
+ "foo=bar; Expires=Fri, 07 Aug 2007 08:04:19 GMT",
+ "foo2=bar2; Expires=Fri, 07 Aug 2017 08:04:19 GMT"
+ ],
+ "sent": [
+ { "name": "foo2", "value": "bar2" }
+ ]
+ },
+ {
+ "test": "0004",
+ "received": [
+ "foo"
+ ],
+ "sent": []
+ },
+ {
+ "test": "0005",
+ "received": [
+ "foo=bar; max-age=10000;"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0006",
+ "received": [
+ "foo=bar; max-age=0;"
+ ],
+ "sent": []
+ },
+ {
+ "test": "0007",
+ "received": [
+ "foo=bar; version=1;"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0008",
+ "received": [
+ "foo=bar; version=1000;"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0009",
+ "received": [
+ "foo=bar; customvalue=1000;"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0010",
+ "received": [
+ "foo=bar; secure;"
+ ],
+ "sent": []
+ },
+ {
+ "test": "0011",
+ "received": [
+ "foo=bar; customvalue=\"1000 or more\";"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0012",
+ "received": [
+ "foo=bar; customvalue=\"no trailing semicolon\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "0013",
+ "received": [
+ "foo=bar",
+ "foo=qux"
+ ],
+ "sent": [
+ { "name": "foo", "value": "qux" }
+ ]
+ },
+ {
+ "test": "0014",
+ "received": [
+ "foo1=bar",
+ "foo2=qux"
+ ],
+ "sent": [
+ { "name": "foo1", "value": "bar" },
+ { "name": "foo2", "value": "qux" }
+ ]
+ },
+ {
+ "test": "0015",
+ "received": [
+ "a=b",
+ "z=y"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" },
+ { "name": "z", "value": "y" }
+ ]
+ },
+ {
+ "test": "0016",
+ "received": [
+ "z=y",
+ "a=b"
+ ],
+ "sent": [
+ { "name": "z", "value": "y" },
+ { "name": "a", "value": "b" }
+ ]
+ },
+ {
+ "test": "0017",
+ "received": [
+ "z=y, a=b"
+ ],
+ "sent": [
+ { "name": "z", "value": "y, a=b" }
+ ]
+ },
+ {
+ "test": "0018",
+ "received": [
+ "z=y; foo=bar, a=b"
+ ],
+ "sent": [
+ { "name": "z", "value": "y" }
+ ]
+ },
+ {
+ "test": "0019",
+ "received": [
+ "foo=b;max-age=3600, c=d;path=/"
+ ],
+ "sent": [
+ { "name": "foo", "value": "b" }
+ ]
+ },
+ {
+ "test": "0020",
+ "received": [
+ "a=b",
+ "=",
+ "c=d"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" },
+ { "name": "c", "value": "d" }
+ ]
+ },
+ {
+ "test": "0021",
+ "received": [
+ "a=b",
+ "=x",
+ "c=d"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" },
+ { "name": "c", "value": "d" }
+ ]
+ },
+ {
+ "test": "0022",
+ "received": [
+ "a=b",
+ "x=",
+ "c=d"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" },
+ { "name": "x", "value": "" },
+ { "name": "c", "value": "d" }
+ ]
+ },
+ {
+ "test": "0023",
+ "received": [
+ "foo",
+ ""
+ ],
+ "sent": []
+ },
+ {
+ "test": "0024",
+ "received": [
+ "foo",
+ "="
+ ],
+ "sent": []
+ },
+ {
+ "test": "0025",
+ "received": [
+ "foo",
+ "; bar"
+ ],
+ "sent": []
+ },
+ {
+ "test": "0026",
+ "received": [
+ "foo",
+ " "
+ ],
+ "sent": []
+ },
+ {
+ "test": "0027",
+ "received": [
+ "foo",
+ "bar"
+ ],
+ "sent": []
+ },
+ {
+ "test": "0028",
+ "received": [
+ "foo",
+ "\t"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0001",
+ "received": [
+ "foo=bar; Secure"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0002",
+ "received": [
+ "foo=bar; seCURe"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0003",
+ "received": [
+ "foo=bar; \"Secure\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0004",
+ "received": [
+ "foo=bar; Secure="
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0005",
+ "received": [
+ "foo=bar; Secure=aaaa"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0006",
+ "received": [
+ "foo=bar; Secure qux"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0007",
+ "received": [
+ "foo=bar; Secure =aaaaa"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0008",
+ "received": [
+ "foo=bar; Secure= aaaaa"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0009",
+ "received": [
+ "foo=bar; Secure; qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0010",
+ "received": [
+ "foo=bar; Secure;qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0011",
+ "received": [
+ "foo=bar; Secure ; qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0012",
+ "received": [
+ "foo=bar; Secure"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0013",
+ "received": [
+ "foo=bar; Secure ;"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0014",
+ "received": [
+ "foo=bar; Path"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0015",
+ "received": [
+ "foo=bar; Path="
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0016",
+ "received": [
+ "foo=bar; Path=/"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0017",
+ "received": [
+ "foo=bar; Path=/qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0018",
+ "received": [
+ "foo=bar; Path =/qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0019",
+ "received": [
+ "foo=bar; Path= /qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0020",
+ "received": [
+ "foo=bar; Path=/qux ; taz"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0021",
+ "received": [
+ "foo=bar; Path=/qux; Path=/"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0022",
+ "received": [
+ "foo=bar; Path=/; Path=/qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0023",
+ "received": [
+ "foo=bar; Path=/qux; Path=/cookie-parser-result"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "ATTRIBUTE0024",
+ "received": [
+ "foo=bar; Path=/cookie-parser-result; Path=/qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0025",
+ "received": [
+ "foo=bar; qux; Secure"
+ ],
+ "sent": []
+ },
+ {
+ "test": "ATTRIBUTE0026",
+ "received": [
+ "foo=bar; qux=\"aaa;bbb\"; Secure"
+ ],
+ "sent": []
+ },
+ {
+ "test": "CHARSET0001",
+ "received": [
+ "foo=\u6625\u8282\u56de\u5bb6\u8def\u00b7\u6625\u8fd0\u5b8c\u5168\u624b\u518c"
+ ],
+ "sent": [
+ { "name": "foo", "value": "\u6625\u8282\u56de\u5bb6\u8def\u00b7\u6625\u8fd0\u5b8c\u5168\u624b\u518c" }
+ ]
+ },
+ {
+ "test": "CHARSET0002",
+ "received": [
+ "\u6625\u8282\u56de=\u5bb6\u8def\u00b7\u6625\u8fd0\u5b8c\u5168\u624b\u518c"
+ ],
+ "sent": [
+ { "name": "\u6625\u8282\u56de", "value": "\u5bb6\u8def\u00b7\u6625\u8fd0\u5b8c\u5168\u624b\u518c" }
+ ]
+ },
+ {
+ "test": "CHARSET0003",
+ "received": [
+ "\u6625\u8282\u56de=\u5bb6\u8def\u00b7\u6625\u8fd0; \u5b8c\u5168\u624b\u518c"
+ ],
+ "sent": [
+ { "name": "\u6625\u8282\u56de", "value": "\u5bb6\u8def\u00b7\u6625\u8fd0" }
+ ]
+ },
+ {
+ "test": "CHARSET0004",
+ "received": [
+ "foo=\"\u6625\u8282\u56de\u5bb6\u8def\u00b7\u6625\u8fd0\u5b8c\u5168\u624b\u518c\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "\"\u6625\u8282\u56de\u5bb6\u8def\u00b7\u6625\u8fd0\u5b8c\u5168\u624b\u518c\"" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0001",
+ "received": [
+ "a=b"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0002",
+ "received": [
+ "aBc=\"zzz \" ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "\"zzz \"" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0003",
+ "received": [
+ "aBc=\"zzz \" ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "\"zzz \"" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0004",
+ "received": [
+ "aBc=\"zz;pp\" ; ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "\"zz" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0005",
+ "received": [
+ "aBc=\"zz ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "\"zz" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0006",
+ "received": [
+ "aBc=\"zzz \" \"ppp\" ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "\"zzz \" \"ppp\"" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0007",
+ "received": [
+ "aBc=\"zzz \" \"ppp\" ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "\"zzz \" \"ppp\"" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0008",
+ "received": [
+ "aBc=A\"B ;"
+ ],
+ "sent": [
+ { "name": "aBc", "value": "A\"B" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0009",
+ "received": [
+ "BLAHHH; path=/;"
+ ],
+ "sent": []
+ },
+ {
+ "test": "CHROMIUM0010",
+ "received": [
+ "\"BLA\\\"HHH\"; path=/;"
+ ],
+ "sent": []
+ },
+ {
+ "test": "CHROMIUM0011",
+ "received": [
+ "a=\"B"
+ ],
+ "sent": [
+ { "name": "a", "value": "\"B" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0012",
+ "received": [
+ "=ABC"
+ ],
+ "sent": []
+ },
+ {
+ "test": "CHROMIUM0013",
+ "received": [
+ "ABC=; path = /"
+ ],
+ "sent": [
+ { "name": "ABC", "value": "" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0014",
+ "received": [
+ " A = BC ;foo;;; bar"
+ ],
+ "sent": [
+ { "name": "A", "value": "BC" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0015",
+ "received": [
+ " A=== BC ;foo;;; bar"
+ ],
+ "sent": [
+ { "name": "A", "value": "== BC" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0016",
+ "received": [
+ "foo=\"zohNumRKgI0oxyhSsV3Z7D\" ; expires=Sun, 18-Apr-2027 21:06:29 GMT ; path=/ ; "
+ ],
+ "sent": [
+ { "name": "foo", "value": "\"zohNumRKgI0oxyhSsV3Z7D\"" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0017",
+ "received": [
+ "foo=zohNumRKgI0oxyhSsV3Z7D ; expires=Sun, 18-Apr-2027 21:06:29 GMT ; path=/ ; "
+ ],
+ "sent": [
+ { "name": "foo", "value": "zohNumRKgI0oxyhSsV3Z7D" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0018",
+ "received": [
+ " "
+ ],
+ "sent": []
+ },
+ {
+ "test": "CHROMIUM0019",
+ "received": [
+ "a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ ],
+ "sent": [
+ { "name": "a", "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }
+ ]
+ },
+ {
+ "test": "CHROMIUM0021",
+ "received": [
+ ""
+ ],
+ "sent": []
+ },
+ {
+ "test": "COMMA0001",
+ "received": [
+ "foo=bar, baz=qux"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar, baz=qux" }
+ ]
+ },
+ {
+ "test": "COMMA0002",
+ "received": [
+ "foo=\"bar, baz=qux\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "\"bar, baz=qux\"" }
+ ]
+ },
+ {
+ "test": "COMMA0003",
+ "received": [
+ "foo=bar; b,az=qux"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "COMMA0004",
+ "received": [
+ "foo=bar; baz=q,ux"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "COMMA0005",
+ "received": [
+ "foo=bar; Max-Age=50,399"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "COMMA0006",
+ "received": [
+ "foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "COMMA0007",
+ "received": [
+ "foo=bar; Expires=Fri 07 Aug 2019 08:04:19 GMT, baz=qux"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DISABLED_CHROMIUM0020",
+ "received": [
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ ],
+ "sent": []
+ },
+ {
+ "test": "DISABLED_CHROMIUM0022",
+ "received": [
+ "AAA=BB\u0000ZYX"
+ ],
+ "sent": [
+ { "name": "AAA", "value": "BB" }
+ ]
+ },
+ {
+ "test": "DISABLED_CHROMIUM0023",
+ "received": [
+ "AAA=BB\rZYX"
+ ],
+ "sent": [
+ { "name": "AAA", "value": "BB" }
+ ]
+ },
+ {
+ "test": "DISABLED_PATH0029",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/bar"
+ ],
+ "sent-to": "/cookie-parser-result/f%6Fo/bar?disabled-path0029",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0001",
+ "received": [
+ "foo=bar; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0001",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0002",
+ "received": [
+ "foo=bar; domain=home.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0002",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0003",
+ "received": [
+ "foo=bar; domain=.home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0003",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0004",
+ "received": [
+ "foo=bar; domain=home.example.org"
+ ],
+ "sent-to": "http://subdomain.home.example.org:8888/cookie-parser-result?domain0004",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0005",
+ "received": [
+ "foo=bar; domain=.home.example.org"
+ ],
+ "sent-to": "http://subdomain.home.example.org:8888/cookie-parser-result?domain0005",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0006",
+ "received": [
+ "foo=bar; domain=.home.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0006",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0007",
+ "received": [
+ "foo=bar; domain=sibling.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0007",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0008",
+ "received": [
+ "foo=bar; domain=.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0008",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0009",
+ "received": [
+ "foo=bar; domain=example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0009",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0010",
+ "received": [
+ "foo=bar; domain=..home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0010",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0011",
+ "received": [
+ "foo=bar; domain=home..example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0011",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0012",
+ "received": [
+ "foo=bar; domain= .home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0012",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0013",
+ "received": [
+ "foo=bar; domain= . home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0013",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0014",
+ "received": [
+ "foo=bar; domain=home.example.org."
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0014",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0015",
+ "received": [
+ "foo=bar; domain=home.example.org.."
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0015",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0016",
+ "received": [
+ "foo=bar; domain=home.example.org ."
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0016",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0017",
+ "received": [
+ "foo=bar; domain=.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0017",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0018",
+ "received": [
+ "foo=bar; domain=.org."
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0018",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0019",
+ "received": [
+ "foo=bar; domain=home.example.org",
+ "foo2=bar2; domain=.home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0019",
+ "sent": [
+ { "name": "foo", "value": "bar" },
+ { "name": "foo2", "value": "bar2" }
+ ]
+ },
+ {
+ "test": "DOMAIN0020",
+ "received": [
+ "foo2=bar2; domain=.home.example.org",
+ "foo=bar; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0020",
+ "sent": [
+ { "name": "foo2", "value": "bar2" },
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0021",
+ "received": [
+ "foo=bar; domain=\"home.example.org\""
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0021",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0022",
+ "received": [
+ "foo=bar; domain=home.example.org",
+ "foo2=bar2; domain=.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0022",
+ "sent": [
+ { "name": "foo", "value": "bar" },
+ { "name": "foo2", "value": "bar2" }
+ ]
+ },
+ {
+ "test": "DOMAIN0023",
+ "received": [
+ "foo2=bar2; domain=.example.org",
+ "foo=bar; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0023",
+ "sent": [
+ { "name": "foo2", "value": "bar2" },
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0024",
+ "received": [
+ "foo=bar; domain=.example.org; domain=home.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0024",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0025",
+ "received": [
+ "foo=bar; domain=home.example.org; domain=.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0025",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0026",
+ "received": [
+ "foo=bar; domain=home.eXaMpLe.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0026",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0027",
+ "received": [
+ "foo=bar; domain=home.example.org:8888"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0027",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0028",
+ "received": [
+ "foo=bar; domain=subdomain.home.example.org"
+ ],
+ "sent-to": "http://subdomain.home.example.org:8888/cookie-parser-result?domain0028",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0029",
+ "received": [
+ "foo=bar"
+ ],
+ "sent-to": "http://subdomain.home.example.org:8888/cookie-parser-result?domain0029",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0031",
+ "received": [
+ "foo=bar; domain=home.example.org; domain=.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0031",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0033",
+ "received": [
+ "foo=bar; domain=home.example.org"
+ ],
+ "sent-to": "http://hoMe.eXaMplE.org:8888/cookie-parser-result?domain0033",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0034",
+ "received": [
+ "foo=bar; domain=home.example.org; domain=home.example.com"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0034",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0035",
+ "received": [
+ "foo=bar; domain=home.example.com; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0035",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0036",
+ "received": [
+ "foo=bar; domain=home.example.org; domain=home.example.com; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0036",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0037",
+ "received": [
+ "foo=bar; domain=home.example.com; domain=home.example.org; domain=home.example.com"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0037",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0038",
+ "received": [
+ "foo=bar; domain=home.example.org; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0038",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0039",
+ "received": [
+ "foo=bar; domain=home.example.org; domain=example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0039",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0040",
+ "received": [
+ "foo=bar; domain=example.org; domain=home.example.org"
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?domain0040",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "DOMAIN0041",
+ "received": [
+ "foo=bar; domain=.sibling.example.org"
+ ],
+ "sent-to": "http://sibling.example.org:8888/cookie-parser-result?domain0041",
+ "sent": []
+ },
+ {
+ "test": "DOMAIN0042",
+ "received": [
+ "foo=bar; domain=.sibling.home.example.org"
+ ],
+ "sent-to": "http://sibling.home.example.org:8888/cookie-parser-result?domain0042",
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0001",
+ "received": [
+ "foo=bar; max-age=-1"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0002",
+ "received": [
+ "foo=bar; max-age=0"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0003",
+ "received": [
+ "foo=bar; expires=Thu, 10 Apr 1980 16:33:12 GMT"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0004",
+ "received": [
+ "foo=bar; max-age=60"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "MOZILLA0005",
+ "received": [
+ "foo=bar; max-age=-20"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0006",
+ "received": [
+ "foo=bar; max-age=60"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "MOZILLA0007",
+ "received": [
+ "foo=bar; expires=Thu, 10 Apr 1980 16:33:12 GMT"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0008",
+ "received": [
+ "foo=bar; max-age=60",
+ "foo1=bar; max-age=60"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" },
+ { "name": "foo1", "value": "bar" }
+ ]
+ },
+ {
+ "test": "MOZILLA0009",
+ "received": [
+ "foo=bar; max-age=60",
+ "foo1=bar; max-age=60",
+ "foo=differentvalue; max-age=0"
+ ],
+ "sent": [
+ { "name": "foo1", "value": "bar" }
+ ]
+ },
+ {
+ "test": "MOZILLA0010",
+ "received": [
+ "foo=bar; max-age=60",
+ "foo1=bar; max-age=60",
+ "foo=differentvalue; max-age=0",
+ "foo2=evendifferentvalue; max-age=0"
+ ],
+ "sent": [
+ { "name": "foo1", "value": "bar" }
+ ]
+ },
+ {
+ "test": "MOZILLA0011",
+ "received": [
+ "test=parser; domain=.parser.test; ;; ;=; ,,, ===,abc,=; abracadabra! max-age=20;=;;"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0012",
+ "received": [
+ "test=\"fubar! = foo;bar\\\";\" parser; max-age=6",
+ "five; max-age=2.63,"
+ ],
+ "sent": [
+ { "name": "test", "value": "\"fubar! = foo" }
+ ]
+ },
+ {
+ "test": "MOZILLA0013",
+ "received": [
+ "test=kill; max-age=0",
+ "five; max-age=0"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0014",
+ "received": [
+ "six"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0015",
+ "received": [
+ "six",
+ "seven"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0016",
+ "received": [
+ "six",
+ "seven",
+ " =eight"
+ ],
+ "sent": []
+ },
+ {
+ "test": "MOZILLA0017",
+ "received": [
+ "six",
+ "seven",
+ " =eight",
+ "test=six"
+ ],
+ "sent": [
+ { "name": "test", "value": "six" }
+ ]
+ },
+ {
+ "test": "NAME0001",
+ "received": [
+ "a=bar"
+ ],
+ "sent": [
+ { "name": "a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0002",
+ "received": [
+ "1=bar"
+ ],
+ "sent": [
+ { "name": "1", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0003",
+ "received": [
+ "$=bar"
+ ],
+ "sent": [
+ { "name": "$", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0004",
+ "received": [
+ "!a=bar"
+ ],
+ "sent": [
+ { "name": "!a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0005",
+ "received": [
+ "@a=bar"
+ ],
+ "sent": [
+ { "name": "@a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0006",
+ "received": [
+ "#a=bar"
+ ],
+ "sent": [
+ { "name": "#a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0007",
+ "received": [
+ "$a=bar"
+ ],
+ "sent": [
+ { "name": "$a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0008",
+ "received": [
+ "%a=bar"
+ ],
+ "sent": [
+ { "name": "%a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0009",
+ "received": [
+ "^a=bar"
+ ],
+ "sent": [
+ { "name": "^a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0010",
+ "received": [
+ "&a=bar"
+ ],
+ "sent": [
+ { "name": "&a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0011",
+ "received": [
+ "*a=bar"
+ ],
+ "sent": [
+ { "name": "*a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0012",
+ "received": [
+ "(a=bar"
+ ],
+ "sent": [
+ { "name": "(a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0013",
+ "received": [
+ ")a=bar"
+ ],
+ "sent": [
+ { "name": ")a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0014",
+ "received": [
+ "-a=bar"
+ ],
+ "sent": [
+ { "name": "-a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0015",
+ "received": [
+ "_a=bar"
+ ],
+ "sent": [
+ { "name": "_a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0016",
+ "received": [
+ "+=bar"
+ ],
+ "sent": [
+ { "name": "+", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0017",
+ "received": [
+ "=a=bar"
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0018",
+ "received": [
+ "a =bar"
+ ],
+ "sent": [
+ { "name": "a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0019",
+ "received": [
+ "\"a=bar"
+ ],
+ "sent": [
+ { "name": "\"a", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0020",
+ "received": [
+ "\"a=b\"=bar"
+ ],
+ "sent": [
+ { "name": "\"a", "value": "b\"=bar" }
+ ]
+ },
+ {
+ "test": "NAME0021",
+ "received": [
+ "\"a=b\"=bar",
+ "\"a=qux"
+ ],
+ "sent": [
+ { "name": "\"a", "value": "qux" }
+ ]
+ },
+ {
+ "test": "NAME0022",
+ "received": [
+ " foo=bar"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0023",
+ "received": [
+ "foo;bar=baz"
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0024",
+ "received": [
+ "$Version=1; foo=bar"
+ ],
+ "sent": [
+ { "name": "$Version", "value": "1" }
+ ]
+ },
+ {
+ "test": "NAME0025",
+ "received": [
+ "===a=bar"
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0026",
+ "received": [
+ "foo=bar "
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0027",
+ "received": [
+ "foo=bar ;"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "NAME0028",
+ "received": [
+ "=a"
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0029",
+ "received": [
+ "="
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0030",
+ "received": [
+ "foo bar=baz"
+ ],
+ "sent": [
+ { "name": "foo bar", "value": "baz" }
+ ]
+ },
+ {
+ "test": "NAME0031",
+ "received": [
+ "\"foo;bar\"=baz"
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0032",
+ "received": [
+ "\"foo\\\"bar;baz\"=qux"
+ ],
+ "sent": []
+ },
+ {
+ "test": "NAME0033",
+ "received": [
+ "=foo=bar",
+ "aaa"
+ ],
+ "sent": []
+ },
+ {
+ "test": "OPTIONAL_DOMAIN0030",
+ "received": [
+ "foo=bar; domain="
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?optional-domain0030",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "OPTIONAL_DOMAIN0041",
+ "received": [
+ "foo=bar; domain=example.org; domain="
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?optional-domain0041",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "OPTIONAL_DOMAIN0042",
+ "received": [
+ "foo=bar; domain=foo.example.org; domain="
+ ],
+ "sent-to": "http://home.example.org:8888/cookie-parser-result?optional-domain0042",
+ "sent": []
+ },
+ {
+ "test": "OPTIONAL_DOMAIN0043",
+ "received": [
+ "foo=bar; domain=foo.example.org; domain="
+ ],
+ "sent-to": "http://subdomain.home.example.org:8888/cookie-parser-result?optional-domain0043",
+ "sent": []
+ },
+ {
+ "test": "ORDERING0001",
+ "received": [
+ "key=val0;",
+ "key=val1; path=/cookie-parser-result",
+ "key=val2; path=/",
+ "key=val3; path=/bar",
+ "key=val4; domain=.example.org",
+ "key=val5; domain=.example.org; path=/cookie-parser-result/foo"
+ ],
+ "sent-to": "/cookie-parser-result/foo/baz?ordering0001",
+ "sent": [
+ { "name": "key", "value": "val5" },
+ { "name": "key", "value": "val1" },
+ { "name": "key", "value": "val2" },
+ { "name": "key", "value": "val4" }
+ ]
+ },
+ {
+ "test": "PATH0001",
+ "received": [
+ "a=b; path=/",
+ "x=y; path=/cookie-parser-result"
+ ],
+ "sent": [
+ { "name": "x", "value": "y" },
+ { "name": "a", "value": "b" }
+ ]
+ },
+ {
+ "test": "PATH0002",
+ "received": [
+ "a=b; path=/cookie-parser-result",
+ "x=y; path=/"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" },
+ { "name": "x", "value": "y" }
+ ]
+ },
+ {
+ "test": "PATH0003",
+ "received": [
+ "x=y; path=/",
+ "a=b; path=/cookie-parser-result"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" },
+ { "name": "x", "value": "y" }
+ ]
+ },
+ {
+ "test": "PATH0004",
+ "received": [
+ "x=y; path=/cookie-parser-result",
+ "a=b; path=/"
+ ],
+ "sent": [
+ { "name": "x", "value": "y" },
+ { "name": "a", "value": "b" }
+ ]
+ },
+ {
+ "test": "PATH0005",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo"
+ ],
+ "sent": []
+ },
+ {
+ "test": "PATH0006",
+ "received": [
+ "foo=bar",
+ "foo=qux; path=/cookie-parser-result/foo"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0007",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo"
+ ],
+ "sent-to": "/cookie-parser-result/foo?path0007",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0008",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo"
+ ],
+ "sent-to": "/cookie-parser-result/bar?path0008",
+ "sent": []
+ },
+ {
+ "test": "PATH0009",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux"
+ ],
+ "sent-to": "/cookie-parser-result/foo?path0009",
+ "sent": []
+ },
+ {
+ "test": "PATH0010",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0010",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0011",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux"
+ ],
+ "sent-to": "/cookie-parser-result/bar/qux?path0011",
+ "sent": []
+ },
+ {
+ "test": "PATH0012",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux"
+ ],
+ "sent-to": "/cookie-parser-result/foo/baz?path0012",
+ "sent": []
+ },
+ {
+ "test": "PATH0013",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux/"
+ ],
+ "sent-to": "/cookie-parser-result/foo/baz?path0013",
+ "sent": []
+ },
+ {
+ "test": "PATH0014",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux/"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0014",
+ "sent": []
+ },
+ {
+ "test": "PATH0015",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux/"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux/?path0015",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0016",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0016",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0017",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/"
+ ],
+ "sent-to": "/cookie-parser-result/foo//qux?path0017",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0018",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/"
+ ],
+ "sent-to": "/cookie-parser-result/fooqux?path0018",
+ "sent": []
+ },
+ {
+ "test": "PATH0019",
+ "received": [
+ "foo=bar; path"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0020",
+ "received": [
+ "foo=bar; path="
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0021",
+ "received": [
+ "foo=bar; path=/"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0022",
+ "received": [
+ "foo=bar; path= /"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0023",
+ "received": [
+ "foo=bar; Path=/cookie-PARSER-result"
+ ],
+ "sent": []
+ },
+ {
+ "test": "PATH0024",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux?"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0024",
+ "sent": []
+ },
+ {
+ "test": "PATH0025",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux#"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0025",
+ "sent": []
+ },
+ {
+ "test": "PATH0026",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/foo/qux;"
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0026",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0027",
+ "received": [
+ "foo=bar; path=\"/cookie-parser-result/foo/qux;\""
+ ],
+ "sent-to": "/cookie-parser-result/foo/qux?path0027",
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0028",
+ "received": [
+ "foo=bar; path=/cookie-parser-result/f%6Fo/bar"
+ ],
+ "sent-to": "/cookie-parser-result/foo/bar?path0028",
+ "sent": []
+ },
+ {
+ "test": "PATH0029",
+ "received": [
+ "a=b; \tpath\t=\t/cookie-parser-result\t",
+ "x=y; \tpath\t=\t/book\t"
+ ],
+ "sent": [
+ { "name": "a", "value": "b" }
+ ]
+ },
+ {
+ "test": "PATH0030",
+ "received": [
+ "foo=bar; path=/dog; path="
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "PATH0031",
+ "received": [
+ "foo=bar; path=; path=/dog"
+ ],
+ "sent": []
+ },
+ {
+ "test": "PATH0032",
+ "received": [
+ "foo=bar; path=/cookie-parser-result",
+ "foo=qux; path=/cookie-parser-result/"
+ ],
+ "sent-to": "/cookie-parser-result/dog?path0032",
+ "sent": [
+ { "name": "foo", "value": "qux" },
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "VALUE0001",
+ "received": [
+ "foo= bar"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ },
+ {
+ "test": "VALUE0002",
+ "received": [
+ "foo=\"bar\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "\"bar\"" }
+ ]
+ },
+ {
+ "test": "VALUE0003",
+ "received": [
+ "foo=\" bar \""
+ ],
+ "sent": [
+ { "name": "foo", "value": "\" bar \"" }
+ ]
+ },
+ {
+ "test": "VALUE0004",
+ "received": [
+ "foo=\"bar;baz\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "\"bar" }
+ ]
+ },
+ {
+ "test": "VALUE0005",
+ "received": [
+ "foo=\"bar=baz\""
+ ],
+ "sent": [
+ { "name": "foo", "value": "\"bar=baz\"" }
+ ]
+ },
+ {
+ "test": "VALUE0006",
+ "received": [
+ "\tfoo\t=\tbar\t \t;\tttt"
+ ],
+ "sent": [
+ { "name": "foo", "value": "bar" }
+ ]
+ }
+]
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var url = require('url');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+var CookieJar = tough.CookieJar;
+
+function readJson(filePath) {
+ filePath = path.join(__dirname, filePath);
+ return JSON.parse(fs.readFileSync(filePath).toString());
+}
+
+function setGetCookieVows() {
+ var theVows = {};
+ var data = readJson('./ietf_data/parser.json');
+
+ data.forEach(function (testCase) {
+ theVows[testCase.test] = function () {
+ var jar = new CookieJar();
+ var expected = testCase['sent']
+ var sentFrom = 'http://home.example.org/cookie-parser?' + testCase.test;
+ var sentTo = testCase['sent-to'] ?
+ url.resolve('http://home.example.org', testCase['sent-to']) :
+ 'http://home.example.org/cookie-parser-result?' + testCase.test;
+
+ testCase['received'].forEach(function (cookieStr) {
+ jar.setCookieSync(cookieStr, sentFrom, {ignoreError: true});
+ });
+
+ var actual = jar.getCookiesSync(sentTo);
+ actual = actual.sort(tough.cookieCompare);
+
+ assert.strictEqual(actual.length, expected.length);
+
+ actual.forEach(function (actualCookie, idx) {
+ var expectedCookie = expected[idx];
+ assert.strictEqual(actualCookie.key, expectedCookie.name);
+ assert.strictEqual(actualCookie.value, expectedCookie.value);
+ });
+ };
+ });
+
+ return {'Set/get cookie tests': theVows};
+}
+
+function dateVows() {
+ var theVows = {};
+
+ [
+ './ietf_data/dates/bsd-examples.json',
+ './ietf_data/dates/examples.json'
+ ].forEach(function (filePath) {
+ var data = readJson(filePath);
+ var fileName = path.basename(filePath);
+
+ data.forEach(function (testCase) {
+ theVows[fileName + ' : ' + testCase.test] = function () {
+ var actual = tough.parseDate(testCase.test);
+ actual = actual ? actual.toUTCString() : null;
+ assert.strictEqual(actual, testCase.expected);
+ };
+ });
+ });
+
+ return {'Date': theVows};
+}
+
+vows
+ .describe('IETF http state tests')
+ .addBatch(setGetCookieVows())
+ .addBatch(dateVows())
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+
+vows
+ .describe('Lifetime')
+ .addBatch({
+ "TTL with max-age": function () {
+ var c = new Cookie();
+ c.maxAge = 123;
+ assert.equal(c.TTL(), 123000);
+ assert.equal(c.expiryTime(new Date(9000000)), 9123000);
+ },
+ "TTL with zero max-age": function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.maxAge = 0; // should be treated as "earliest representable"
+ assert.equal(c.TTL(), 0);
+ assert.equal(c.expiryTime(new Date(9000000)), -Infinity);
+ assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT
+ },
+ "TTL with negative max-age": function () {
+ var c = new Cookie();
+ c.key = 'a';
+ c.value = 'b';
+ c.maxAge = -1; // should be treated as "earliest representable"
+ assert.equal(c.TTL(), 0);
+ assert.equal(c.expiryTime(new Date(9000000)), -Infinity);
+ assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT
+ },
+ "TTL with max-age and expires": function () {
+ var c = new Cookie();
+ c.maxAge = 123;
+ c.expires = new Date(Date.now() + 9000);
+ assert.equal(c.TTL(), 123000);
+ assert.ok(c.isPersistent());
+ },
+ "TTL with expires": function () {
+ var c = new Cookie();
+ var now = Date.now();
+ c.expires = new Date(now + 9000);
+ assert.equal(c.TTL(now), 9000);
+ assert.equal(c.expiryTime(), c.expires.getTime());
+ },
+ "TTL with old expires": function () {
+ var c = new Cookie();
+ c.setExpires('17 Oct 2010 00:00:00 GMT');
+ assert.ok(c.TTL() < 0);
+ assert.ok(c.isPersistent());
+ },
+ "default TTL": {
+ topic: function () {
+ return new Cookie();
+ },
+ "is Infinite-future": function (c) {
+ assert.equal(c.TTL(), Infinity)
+ },
+ "is a 'session' cookie": function (c) {
+ assert.ok(!c.isPersistent())
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+
+vows
+ .describe('Parsing')
+ .addBatch({
+ "simple": {
+ topic: function() {
+ return Cookie.parse('a=bcd') || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'a') },
+ "value": function(c) { assert.equal(c.value, 'bcd') },
+ "no path": function(c) { assert.equal(c.path, null) },
+ "no domain": function(c) { assert.equal(c.domain, null) },
+ "no extensions": function(c) { assert.ok(!c.extensions) }
+ },
+ "with expiry": {
+ topic: function() {
+ return Cookie.parse('a=bcd; Expires=Tue, 18 Oct 2011 07:05:03 GMT') || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'a') },
+ "value": function(c) { assert.equal(c.value, 'bcd') },
+ "has expires": function(c) {
+ assert.ok(c.expires !== Infinity, 'expiry is infinite when it shouldn\'t be');
+ assert.equal(c.expires.getTime(), 1318921503000);
+ }
+ },
+ "with expiry and path": {
+ topic: function() {
+ return Cookie.parse('abc="xyzzy!"; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Path=/aBc') || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'abc') },
+ "value": function(c) { assert.equal(c.value, '"xyzzy!"') },
+ "has expires": function(c) {
+ assert.ok(c.expires !== Infinity, 'expiry is infinite when it shouldn\'t be');
+ assert.equal(c.expires.getTime(), 1318921503000);
+ },
+ "has path": function(c) { assert.equal(c.path, '/aBc'); },
+ "no httponly or secure": function(c) {
+ assert.ok(!c.httpOnly);
+ assert.ok(!c.secure);
+ }
+ },
+ "with everything": {
+ topic: function() {
+ return Cookie.parse('abc="xyzzy!"; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Path=/aBc; Domain=example.com; Secure; HTTPOnly; Max-Age=1234; Foo=Bar; Baz') || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'abc') },
+ "value": function(c) { assert.equal(c.value, '"xyzzy!"') },
+ "has expires": function(c) {
+ assert.ok(c.expires !== Infinity, 'expiry is infinite when it shouldn\'t be');
+ assert.equal(c.expires.getTime(), 1318921503000);
+ },
+ "has path": function(c) { assert.equal(c.path, '/aBc'); },
+ "has domain": function(c) { assert.equal(c.domain, 'example.com'); },
+ "has httponly": function(c) { assert.equal(c.httpOnly, true); },
+ "has secure": function(c) { assert.equal(c.secure, true); },
+ "has max-age": function(c) { assert.equal(c.maxAge, 1234); },
+ "has extensions": function(c) {
+ assert.ok(c.extensions);
+ assert.equal(c.extensions[0], 'Foo=Bar');
+ assert.equal(c.extensions[1], 'Baz');
+ }
+ },
+ "invalid expires": function() {
+ var c = Cookie.parse("a=b; Expires=xyzzy");
+ assert.ok(c);
+ assert.equal(c.expires, Infinity);
+ },
+ "zero max-age": function() {
+ var c = Cookie.parse("a=b; Max-Age=0");
+ assert.ok(c);
+ assert.equal(c.maxAge, 0);
+ },
+ "negative max-age": function() {
+ var c = Cookie.parse("a=b; Max-Age=-1");
+ assert.ok(c);
+ assert.equal(c.maxAge, -1);
+ },
+ "empty domain": function() {
+ var c = Cookie.parse("a=b; domain=");
+ assert.ok(c);
+ assert.equal(c.domain, null);
+ },
+ "dot domain": function() {
+ var c = Cookie.parse("a=b; domain=.");
+ assert.ok(c);
+ assert.equal(c.domain, null);
+ },
+ "uppercase domain": function() {
+ var c = Cookie.parse("a=b; domain=EXAMPLE.COM");
+ assert.ok(c);
+ assert.equal(c.domain, 'example.com');
+ },
+ "trailing dot in domain": {
+ topic: function() {
+ return Cookie.parse("a=b; Domain=example.com.", true) || null;
+ },
+ "has the domain": function(c) { assert.equal(c.domain,"example.com.") },
+ "but doesn't validate": function(c) { assert.equal(c.validate(),false) }
+ },
+ "empty path": function() {
+ var c = Cookie.parse("a=b; path=");
+ assert.ok(c);
+ assert.equal(c.path, null);
+ },
+ "no-slash path": function() {
+ var c = Cookie.parse("a=b; path=xyzzy");
+ assert.ok(c);
+ assert.equal(c.path, null);
+ },
+ "trailing semi-colons after path": {
+ topic: function () {
+ return [
+ "a=b; path=/;",
+ "c=d;;;;"
+ ];
+ },
+ "strips semi-colons": function (t) {
+ var c1 = Cookie.parse(t[0]);
+ var c2 = Cookie.parse(t[1]);
+ assert.ok(c1);
+ assert.ok(c2);
+ assert.equal(c1.path, '/');
+ }
+ },
+ "secure-with-value": function() {
+ var c = Cookie.parse("a=b; Secure=xyzzy");
+ assert.ok(c);
+ assert.equal(c.secure, true);
+ },
+ "httponly-with-value": function() {
+ var c = Cookie.parse("a=b; HttpOnly=xyzzy");
+ assert.ok(c);
+ assert.equal(c.httpOnly, true);
+ },
+ "garbage": {
+ topic: function() {
+ return Cookie.parse("\x08", true) || null;
+ },
+ "doesn't parse": function(c) { assert.equal(c,null) }
+ },
+ "public suffix domain": {
+ topic: function() {
+ return Cookie.parse("a=b; domain=kyoto.jp", true) || null;
+ },
+ "parses fine": function(c) {
+ assert.ok(c);
+ assert.equal(c.domain, 'kyoto.jp');
+ },
+ "but fails validation": function(c) {
+ assert.ok(c);
+ assert.ok(!c.validate());
+ }
+ },
+ "public suffix foonet.net": {
+ "top level": {
+ topic: function() {
+ return Cookie.parse("a=b; domain=foonet.net") || null;
+ },
+ "parses and is valid": function(c) {
+ assert.ok(c);
+ assert.equal(c.domain, 'foonet.net');
+ assert.ok(c.validate());
+ }
+ },
+ "www": {
+ topic: function() {
+ return Cookie.parse("a=b; domain=www.foonet.net") || null;
+ },
+ "parses and is valid": function(c) {
+ assert.ok(c);
+ assert.equal(c.domain, 'www.foonet.net');
+ assert.ok(c.validate());
+ }
+ },
+ "with a dot": {
+ topic: function() {
+ return Cookie.parse("a=b; domain=.foonet.net") || null;
+ },
+ "parses and is valid": function(c) {
+ assert.ok(c);
+ assert.equal(c.domain, 'foonet.net');
+ assert.ok(c.validate());
+ }
+ }
+ },
+ "Ironically, Google 'GAPS' cookie has very little whitespace": {
+ topic: function() {
+ return Cookie.parse("GAPS=1:A1aaaaAaAAa1aaAaAaaAAAaaa1a11a:aaaAaAaAa-aaaA1-;Path=/;Expires=Thu, 17-Apr-2014 02:12:29 GMT;Secure;HttpOnly");
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'GAPS') },
+ "value": function(c) { assert.equal(c.value, '1:A1aaaaAaAAa1aaAaAaaAAAaaa1a11a:aaaAaAaAa-aaaA1-') },
+ "path": function(c) {
+ assert.notEqual(c.path, '/;Expires'); // BUG
+ assert.equal(c.path, '/');
+ },
+ "expires": function(c) {
+ assert.notEqual(c.expires, Infinity);
+ assert.equal(c.expires.getTime(), 1397700749000);
+ },
+ "secure": function(c) { assert.ok(c.secure) },
+ "httponly": function(c) { assert.ok(c.httpOnly) }
+ },
+ "lots of equal signs": {
+ topic: function() {
+ return Cookie.parse("queryPref=b=c&d=e; Path=/f=g; Expires=Thu, 17 Apr 2014 02:12:29 GMT; HttpOnly");
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'queryPref') },
+ "value": function(c) { assert.equal(c.value, 'b=c&d=e') },
+ "path": function(c) {
+ assert.equal(c.path, '/f=g');
+ },
+ "expires": function(c) {
+ assert.notEqual(c.expires, Infinity);
+ assert.equal(c.expires.getTime(), 1397700749000);
+ },
+ "httponly": function(c) { assert.ok(c.httpOnly) }
+ },
+ "spaces in value": {
+ topic: function() {
+ return Cookie.parse('a=one two three',false) || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'a') },
+ "value": function(c) { assert.equal(c.value, 'one two three') },
+ "no path": function(c) { assert.equal(c.path, null) },
+ "no domain": function(c) { assert.equal(c.domain, null) },
+ "no extensions": function(c) { assert.ok(!c.extensions) }
+ },
+ "quoted spaces in value": {
+ topic: function() {
+ return Cookie.parse('a="one two three"',false) || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'a') },
+ "value": function(c) { assert.equal(c.value, '"one two three"') },
+ "no path": function(c) { assert.equal(c.path, null) },
+ "no domain": function(c) { assert.equal(c.domain, null) },
+ "no extensions": function(c) { assert.ok(!c.extensions) }
+ },
+ "non-ASCII in value": {
+ topic: function() {
+ return Cookie.parse('farbe=weiß',false) || null;
+ },
+ "parsed": function(c) { assert.ok(c) },
+ "key": function(c) { assert.equal(c.key, 'farbe') },
+ "value": function(c) { assert.equal(c.value, 'weiß') },
+ "no path": function(c) { assert.equal(c.path, null) },
+ "no domain": function(c) { assert.equal(c.domain, null) },
+ "no extensions": function(c) { assert.ok(!c.extensions) }
+ }
+ })
+ .export(module);
--- /dev/null
+/*!
+ * Copyright (c) 2015, Salesforce.com, 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:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of Salesforce.com 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+ */
+
+'use strict';
+var vows = require('vows');
+var assert = require('assert');
+var async = require('async');
+var tough = require('../lib/cookie');
+var Cookie = tough.Cookie;
+var CookieJar = tough.CookieJar;
+
+var atNow = Date.now();
+
+function at(offset) {
+ return {now: new Date(atNow + offset)};
+}
+
+vows
+ .describe('Regression tests')
+ .addBatch({
+ "Issue 1": {
+ topic: function () {
+ var cj = new CookieJar();
+ cj.setCookie('hello=world; path=/some/path/', 'http://domain/some/path/file', function (err, cookie) {
+ this.callback(err, {cj: cj, cookie: cookie});
+ }.bind(this));
+ },
+ "stored a cookie": function (t) {
+ assert.ok(t.cookie);
+ },
+ "getting it back": {
+ topic: function (t) {
+ t.cj.getCookies('http://domain/some/path/file', function (err, cookies) {
+ this.callback(err, {cj: t.cj, cookies: cookies || []});
+ }.bind(this));
+ },
+ "got one cookie": function (t) {
+ assert.lengthOf(t.cookies, 1);
+ },
+ "it's the right one": function (t) {
+ var c = t.cookies[0];
+ assert.equal(c.key, 'hello');
+ assert.equal(c.value, 'world');
+ }
+ }
+ }
+ })
+ .addBatch({
+ "trailing semi-colon set into cj": {
+ topic: function () {
+ var cb = this.callback;
+ var cj = new CookieJar();
+ var ex = 'http://www.example.com';
+ var tasks = [];
+ tasks.push(function (next) {
+ cj.setCookie('broken_path=testme; path=/;', ex, at(-1), next);
+ });
+ tasks.push(function (next) {
+ cj.setCookie('b=2; Path=/;;;;', ex, at(-1), next);
+ });
+ async.parallel(tasks, function (err, cookies) {
+ cb(null, {
+ cj: cj,
+ cookies: cookies
+ });
+ });
+ },
+ "check number of cookies": function (t) {
+ assert.lengthOf(t.cookies, 2, "didn't set");
+ },
+ "check *broken_path* was set properly": function (t) {
+ assert.equal(t.cookies[0].key, "broken_path");
+ assert.equal(t.cookies[0].value, "testme");
+ assert.equal(t.cookies[0].path, "/");
+ },
+ "check *b* was set properly": function (t) {
+ assert.equal(t.cookies[1].key, "b");
+ assert.equal(t.cookies[1].value, "2");
+ assert.equal(t.cookies[1].path, "/");
+ },
+ "retrieve the cookie": {
+ topic: function (t) {
+ var cb = this.callback;
+ t.cj.getCookies('http://www.example.com', {}, function (err, cookies) {
+ t.cookies = cookies;
+ cb(err, t);
+ });
+ },
+ "get the cookie": function (t) {
+ assert.lengthOf(t.cookies, 2);
+ assert.equal(t.cookies[0].key, 'broken_path');
+ assert.equal(t.cookies[0].value, 'testme');
+ assert.equal(t.cookies[1].key, "b");
+ assert.equal(t.cookies[1].value, "2");
+ assert.equal(t.cookies[1].path, "/");
+ }
+ }
+ }
+ })
+ .addBatch({
+ "tough-cookie throws exception on malformed URI (GH-32)": {
+ topic: function () {
+ var url = "http://www.example.com/?test=100%";
+ var cj = new CookieJar();
+
+ cj.setCookieSync("Test=Test", url);
+
+ return cj.getCookieStringSync(url);
+ },
+ "cookies are set": function (cookieStr) {
+ assert.strictEqual(cookieStr, "Test=Test");
+ }
+ }
+ })
+ .export(module);
--- /dev/null
+async (0.1.18) MIT (http://github.com/caolan/async/raw/master/LICENSE) node_modules/async/package.json
+diff (1.0.8) BSD (http://github.com/kpdecker/jsdiff/blob/master/LICENSE) node_modules/vows/node_modules/diff/package.json
+eyes (0.1.8) MIT License (https://spdx.org/licenses/MIT) node_modules/vows/node_modules/eyes/package.json
+punycode (1.0.0) MIT (http://mths.be/mit) node_modules/punycode/package.json
+tough-cookie (0.12.1) MIT License (https://spdx.org/licenses/MIT) package.json
+vows (0.7.0) MIT License (https://spdx.org/licenses/MIT) node_modules/vows/LICENSE
--- /dev/null
+{
+ "node": true,
+ "asi": true,
+ "laxcomma": true
+}
--- /dev/null
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
--- /dev/null
+tunnel-agent
+============
+
+HTTP proxy tunneling agent. Formerly part of mikeal/request, now a standalone module.
--- /dev/null
+'use strict'
+
+var net = require('net')
+ , tls = require('tls')
+ , http = require('http')
+ , https = require('https')
+ , events = require('events')
+ , assert = require('assert')
+ , util = require('util')
+ ;
+
+exports.httpOverHttp = httpOverHttp
+exports.httpsOverHttp = httpsOverHttp
+exports.httpOverHttps = httpOverHttps
+exports.httpsOverHttps = httpsOverHttps
+
+
+function httpOverHttp(options) {
+ var agent = new TunnelingAgent(options)
+ agent.request = http.request
+ return agent
+}
+
+function httpsOverHttp(options) {
+ var agent = new TunnelingAgent(options)
+ agent.request = http.request
+ agent.createSocket = createSecureSocket
+ return agent
+}
+
+function httpOverHttps(options) {
+ var agent = new TunnelingAgent(options)
+ agent.request = https.request
+ return agent
+}
+
+function httpsOverHttps(options) {
+ var agent = new TunnelingAgent(options)
+ agent.request = https.request
+ agent.createSocket = createSecureSocket
+ return agent
+}
+
+
+function TunnelingAgent(options) {
+ var self = this
+ self.options = options || {}
+ self.proxyOptions = self.options.proxy || {}
+ self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets
+ self.requests = []
+ self.sockets = []
+
+ self.on('free', function onFree(socket, host, port) {
+ for (var i = 0, len = self.requests.length; i < len; ++i) {
+ var pending = self.requests[i]
+ if (pending.host === host && pending.port === port) {
+ // Detect the request to connect same origin server,
+ // reuse the connection.
+ self.requests.splice(i, 1)
+ pending.request.onSocket(socket)
+ return
+ }
+ }
+ socket.destroy()
+ self.removeSocket(socket)
+ })
+}
+util.inherits(TunnelingAgent, events.EventEmitter)
+
+TunnelingAgent.prototype.addRequest = function addRequest(req, options) {
+ var self = this
+
+ // Legacy API: addRequest(req, host, port, path)
+ if (typeof options === 'string') {
+ options = {
+ host: options,
+ port: arguments[2],
+ path: arguments[3]
+ };
+ }
+
+ if (self.sockets.length >= this.maxSockets) {
+ // We are over limit so we'll add it to the queue.
+ self.requests.push({host: host, port: port, request: req})
+ return
+ }
+
+ // If we are under maxSockets create a new one.
+ self.createSocket({host: options.host, port: options.port, request: req}, function(socket) {
+ socket.on('free', onFree)
+ socket.on('close', onCloseOrRemove)
+ socket.on('agentRemove', onCloseOrRemove)
+ req.onSocket(socket)
+
+ function onFree() {
+ self.emit('free', socket, options.host, options.port)
+ }
+
+ function onCloseOrRemove(err) {
+ self.removeSocket()
+ socket.removeListener('free', onFree)
+ socket.removeListener('close', onCloseOrRemove)
+ socket.removeListener('agentRemove', onCloseOrRemove)
+ }
+ })
+}
+
+TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
+ var self = this
+ var placeholder = {}
+ self.sockets.push(placeholder)
+
+ var connectOptions = mergeOptions({}, self.proxyOptions,
+ { method: 'CONNECT'
+ , path: options.host + ':' + options.port
+ , agent: false
+ }
+ )
+ if (connectOptions.proxyAuth) {
+ connectOptions.headers = connectOptions.headers || {}
+ connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
+ new Buffer(connectOptions.proxyAuth).toString('base64')
+ }
+
+ debug('making CONNECT request')
+ var connectReq = self.request(connectOptions)
+ connectReq.useChunkedEncodingByDefault = false // for v0.6
+ connectReq.once('response', onResponse) // for v0.6
+ connectReq.once('upgrade', onUpgrade) // for v0.6
+ connectReq.once('connect', onConnect) // for v0.7 or later
+ connectReq.once('error', onError)
+ connectReq.end()
+
+ function onResponse(res) {
+ // Very hacky. This is necessary to avoid http-parser leaks.
+ res.upgrade = true
+ }
+
+ function onUpgrade(res, socket, head) {
+ // Hacky.
+ process.nextTick(function() {
+ onConnect(res, socket, head)
+ })
+ }
+
+ function onConnect(res, socket, head) {
+ connectReq.removeAllListeners()
+ socket.removeAllListeners()
+
+ if (res.statusCode === 200) {
+ assert.equal(head.length, 0)
+ debug('tunneling connection has established')
+ self.sockets[self.sockets.indexOf(placeholder)] = socket
+ cb(socket)
+ } else {
+ debug('tunneling socket could not be established, statusCode=%d', res.statusCode)
+ var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode)
+ error.code = 'ECONNRESET'
+ options.request.emit('error', error)
+ self.removeSocket(placeholder)
+ }
+ }
+
+ function onError(cause) {
+ connectReq.removeAllListeners()
+
+ debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack)
+ var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message)
+ error.code = 'ECONNRESET'
+ options.request.emit('error', error)
+ self.removeSocket(placeholder)
+ }
+}
+
+TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
+ var pos = this.sockets.indexOf(socket)
+ if (pos === -1) return
+
+ this.sockets.splice(pos, 1)
+
+ var pending = this.requests.shift()
+ if (pending) {
+ // If we have pending requests and a socket gets closed a new one
+ // needs to be created to take over in the pool for the one that closed.
+ this.createSocket(pending, function(socket) {
+ pending.request.onSocket(socket)
+ })
+ }
+}
+
+function createSecureSocket(options, cb) {
+ var self = this
+ TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
+ // 0 is dummy port for v0.6
+ var secureSocket = tls.connect(0, mergeOptions({}, self.options,
+ { servername: options.host
+ , socket: socket
+ }
+ ))
+ cb(secureSocket)
+ })
+}
+
+
+function mergeOptions(target) {
+ for (var i = 1, len = arguments.length; i < len; ++i) {
+ var overrides = arguments[i]
+ if (typeof overrides === 'object') {
+ var keys = Object.keys(overrides)
+ for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
+ var k = keys[j]
+ if (overrides[k] !== undefined) {
+ target[k] = overrides[k]
+ }
+ }
+ }
+ }
+ return target
+}
+
+
+var debug
+if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
+ debug = function() {
+ var args = Array.prototype.slice.call(arguments)
+ if (typeof args[0] === 'string') {
+ args[0] = 'TUNNEL: ' + args[0]
+ } else {
+ args.unshift('TUNNEL:')
+ }
+ console.error.apply(console, args)
+ }
+} else {
+ debug = function() {}
+}
+exports.debug = debug // for test
--- /dev/null
+{
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com",
+ "url": "http://www.futurealoof.com"
+ },
+ "name": "tunnel-agent",
+ "description": "HTTP proxy tunneling agent. Formerly part of mikeal/request, now a standalone module.",
+ "version": "0.4.0",
+ "repository": {
+ "url": "git+https://github.com/mikeal/tunnel-agent.git"
+ },
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "bugs": {
+ "url": "https://github.com/mikeal/tunnel-agent/issues"
+ },
+ "homepage": "https://github.com/mikeal/tunnel-agent",
+ "_id": "tunnel-agent@0.4.0",
+ "dist": {
+ "shasum": "b1184e312ffbcf70b3b4c78e8c219de7ebb1c550",
+ "tarball": "http://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz"
+ },
+ "_from": "tunnel-agent@>=0.4.0 <0.5.0",
+ "_npmVersion": "1.3.21",
+ "_npmUser": {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "b1184e312ffbcf70b3b4c78e8c219de7ebb1c550",
+ "_resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz",
+ "readme": "ERROR: No README data found!",
+ "scripts": {}
+}
--- /dev/null
+{
+ "name": "request",
+ "description": "Simplified HTTP request client.",
+ "tags": [
+ "http",
+ "simple",
+ "util",
+ "utility"
+ ],
+ "version": "2.55.0",
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/request/request.git"
+ },
+ "bugs": {
+ "url": "http://github.com/request/request/issues"
+ },
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "main": "index.js",
+ "dependencies": {
+ "bl": "~0.9.0",
+ "caseless": "~0.9.0",
+ "forever-agent": "~0.6.0",
+ "form-data": "~0.2.0",
+ "json-stringify-safe": "~5.0.0",
+ "mime-types": "~2.0.1",
+ "node-uuid": "~1.4.0",
+ "qs": "~2.4.0",
+ "tunnel-agent": "~0.4.0",
+ "tough-cookie": ">=0.12.0",
+ "http-signature": "~0.10.0",
+ "oauth-sign": "~0.6.0",
+ "hawk": "~2.3.0",
+ "aws-sign2": "~0.5.0",
+ "stringstream": "~0.0.4",
+ "combined-stream": "~0.0.5",
+ "isstream": "~0.1.1",
+ "har-validator": "^1.4.0"
+ },
+ "scripts": {
+ "test": "npm run lint && node node_modules/.bin/taper tests/test-*.js && npm run test-browser",
+ "test-browser": "node tests/browser/start.js",
+ "lint": "node node_modules/.bin/eslint lib/ *.js tests/ && echo Lint passed."
+ },
+ "devDependencies": {
+ "browserify": "~5.9.1",
+ "browserify-istanbul": "~0.1.3",
+ "coveralls": "~2.11.2",
+ "eslint": "0.17.1",
+ "function-bind": "~1.0.0",
+ "istanbul": "~0.3.2",
+ "karma": "~0.12.21",
+ "karma-browserify": "~3.0.1",
+ "karma-cli": "0.0.4",
+ "karma-coverage": "0.2.6",
+ "karma-phantomjs-launcher": "~0.1.4",
+ "karma-tap": "~1.0.1",
+ "rimraf": "~2.2.8",
+ "server-destroy": "~1.0.0",
+ "tape": "~3.0.0",
+ "taper": "~0.4.0",
+ "bluebird": "~2.9.21"
+ },
+ "gitHead": "b6000376387db12d0c2d7ed9ee87b0ba123e36dc",
+ "homepage": "https://github.com/request/request",
+ "_id": "request@2.55.0",
+ "_shasum": "d75c1cdf679d76bb100f9bffe1fe551b5c24e93d",
+ "_from": "request@>=2.51.0 <3.0.0",
+ "_npmVersion": "1.4.28",
+ "_npmUser": {
+ "name": "simov",
+ "email": "simeonvelichkov@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mikeal",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ {
+ "name": "nylen",
+ "email": "jnylen@gmail.com"
+ },
+ {
+ "name": "fredkschott",
+ "email": "fkschott@gmail.com"
+ },
+ {
+ "name": "simov",
+ "email": "simeonvelichkov@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "d75c1cdf679d76bb100f9bffe1fe551b5c24e93d",
+ "tarball": "http://registry.npmjs.org/request/-/request-2.55.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/request/-/request-2.55.0.tgz"
+}
--- /dev/null
+#!/bin/sh
+
+if [ -z "`which github-changes`" ]; then
+ # specify version because github-changes "is under heavy development. Things
+ # may break between releases" until 0.1.0
+ echo "First, do: [sudo] npm install -g github-changes@0.0.14"
+ exit 1
+fi
+
+if [ -d .git/refs/remotes/upstream ]; then
+ remote=upstream
+else
+ remote=origin
+fi
+
+# Increment v2.x.y -> v2.x+1.0
+npm version minor || exit 1
+
+# Generate changelog from pull requests
+github-changes -o request -r request \
+ --auth --verbose \
+ --file CHANGELOG.md \
+ --only-pulls --use-commit-body \
+ --date-format '(YYYY/MM/DD)' \
+ || exit 1
+
+# Since the tag for the new version hasn't been pushed yet, any changes in it
+# will be marked as "upcoming"
+version="$(grep '"version"' package.json | cut -d'"' -f4)"
+sed -i -e "s/^### upcoming/### v$version/" CHANGELOG.md
+
+# This may fail if no changelog updates
+# TODO: would this ever actually happen? handle it better?
+git add CHANGELOG.md; git commit -m 'Update changelog'
+
+# Publish the new version to npm
+npm publish || exit 1
+
+# Increment v2.x.0 -> v2.x.1
+# For rationale, see:
+# https://github.com/request/oauth-sign/issues/10#issuecomment-58917018
+npm version patch || exit 1
+
+# Push back to the main repo
+git push $remote master --tags || exit 1
--- /dev/null
+'use strict'
+
+var http = require('http')
+ , https = require('https')
+ , url = require('url')
+ , util = require('util')
+ , stream = require('stream')
+ , qs = require('qs')
+ , querystring = require('querystring')
+ , zlib = require('zlib')
+ , helpers = require('./lib/helpers')
+ , bl = require('bl')
+ , hawk = require('hawk')
+ , aws = require('aws-sign2')
+ , httpSignature = require('http-signature')
+ , mime = require('mime-types')
+ , tunnel = require('tunnel-agent')
+ , stringstream = require('stringstream')
+ , caseless = require('caseless')
+ , ForeverAgent = require('forever-agent')
+ , FormData = require('form-data')
+ , cookies = require('./lib/cookies')
+ , copy = require('./lib/copy')
+ , getProxyFromURI = require('./lib/getProxyFromURI')
+ , Har = require('./lib/har').Har
+ , Auth = require('./lib/auth').Auth
+ , OAuth = require('./lib/oauth').OAuth
+ , Multipart = require('./lib/multipart').Multipart
+ , Redirect = require('./lib/redirect').Redirect
+
+var safeStringify = helpers.safeStringify
+ , isReadStream = helpers.isReadStream
+ , toBase64 = helpers.toBase64
+ , defer = helpers.defer
+ , globalCookieJar = cookies.jar()
+
+
+var globalPool = {}
+
+var defaultProxyHeaderWhiteList = [
+ 'accept',
+ 'accept-charset',
+ 'accept-encoding',
+ 'accept-language',
+ 'accept-ranges',
+ 'cache-control',
+ 'content-encoding',
+ 'content-language',
+ 'content-length',
+ 'content-location',
+ 'content-md5',
+ 'content-range',
+ 'content-type',
+ 'connection',
+ 'date',
+ 'expect',
+ 'max-forwards',
+ 'pragma',
+ 'referer',
+ 'te',
+ 'transfer-encoding',
+ 'user-agent',
+ 'via'
+]
+
+var defaultProxyHeaderExclusiveList = [
+ 'proxy-authorization'
+]
+
+function filterForNonReserved(reserved, options) {
+ // Filter out properties that are not reserved.
+ // Reserved values are passed in at call site.
+
+ var object = {}
+ for (var i in options) {
+ var notReserved = (reserved.indexOf(i) === -1)
+ if (notReserved) {
+ object[i] = options[i]
+ }
+ }
+ return object
+}
+
+function filterOutReservedFunctions(reserved, options) {
+ // Filter out properties that are functions and are reserved.
+ // Reserved values are passed in at call site.
+
+ var object = {}
+ for (var i in options) {
+ var isReserved = !(reserved.indexOf(i) === -1)
+ var isFunction = (typeof options[i] === 'function')
+ if (!(isReserved && isFunction)) {
+ object[i] = options[i]
+ }
+ }
+ return object
+
+}
+
+function constructProxyHost(uriObject) {
+ var port = uriObject.portA
+ , protocol = uriObject.protocol
+ , proxyHost = uriObject.hostname + ':'
+
+ if (port) {
+ proxyHost += port
+ } else if (protocol === 'https:') {
+ proxyHost += '443'
+ } else {
+ proxyHost += '80'
+ }
+
+ return proxyHost
+}
+
+function constructProxyHeaderWhiteList(headers, proxyHeaderWhiteList) {
+ var whiteList = proxyHeaderWhiteList
+ .reduce(function (set, header) {
+ set[header.toLowerCase()] = true
+ return set
+ }, {})
+
+ return Object.keys(headers)
+ .filter(function (header) {
+ return whiteList[header.toLowerCase()]
+ })
+ .reduce(function (set, header) {
+ set[header] = headers[header]
+ return set
+ }, {})
+}
+
+function getTunnelOption(self, options) {
+ // Tunnel HTTPS by default, or if a previous request in the redirect chain
+ // was tunneled. Allow the user to override this setting.
+
+ // If self.tunnel is already set (because this is a redirect), use the
+ // existing value.
+ if (typeof self.tunnel !== 'undefined') {
+ return self.tunnel
+ }
+
+ // If options.tunnel is set (the user specified a value), use it.
+ if (typeof options.tunnel !== 'undefined') {
+ return options.tunnel
+ }
+
+ // If the destination is HTTPS, tunnel.
+ if (self.uri.protocol === 'https:') {
+ return true
+ }
+
+ // Otherwise, leave tunnel unset, because if a later request in the redirect
+ // chain is HTTPS then that request (and any subsequent ones) should be
+ // tunneled.
+ return undefined
+}
+
+function constructTunnelOptions(request) {
+ var proxy = request.proxy
+
+ var tunnelOptions = {
+ proxy : {
+ host : proxy.hostname,
+ port : +proxy.port,
+ proxyAuth : proxy.auth,
+ headers : request.proxyHeaders
+ },
+ headers : request.headers,
+ ca : request.ca,
+ cert : request.cert,
+ key : request.key,
+ passphrase : request.passphrase,
+ pfx : request.pfx,
+ ciphers : request.ciphers,
+ rejectUnauthorized : request.rejectUnauthorized,
+ secureOptions : request.secureOptions,
+ secureProtocol : request.secureProtocol
+ }
+
+ return tunnelOptions
+}
+
+function constructTunnelFnName(uri, proxy) {
+ var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http')
+ var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http')
+ return [uriProtocol, proxyProtocol].join('Over')
+}
+
+function getTunnelFn(request) {
+ var uri = request.uri
+ var proxy = request.proxy
+ var tunnelFnName = constructTunnelFnName(uri, proxy)
+ return tunnel[tunnelFnName]
+}
+
+// Function for properly handling a connection error
+function connectionErrorHandler(error) {
+ var socket = this
+ if (socket.res) {
+ if (socket.res.request) {
+ socket.res.request.emit('error', error)
+ } else {
+ socket.res.emit('error', error)
+ }
+ } else {
+ socket._httpMessage.emit('error', error)
+ }
+}
+
+// Return a simpler request object to allow serialization
+function requestToJSON() {
+ var self = this
+ return {
+ uri: self.uri,
+ method: self.method,
+ headers: self.headers
+ }
+}
+
+// Return a simpler response object to allow serialization
+function responseToJSON() {
+ var self = this
+ return {
+ statusCode: self.statusCode,
+ body: self.body,
+ headers: self.headers,
+ request: requestToJSON.call(self.request)
+ }
+}
+
+// encode rfc3986 characters
+function rfc3986 (str) {
+ return str.replace(/[!'()*]/g, function(c) {
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase()
+ })
+}
+
+function Request (options) {
+ // if given the method property in options, set property explicitMethod to true
+
+ // extend the Request instance with any non-reserved properties
+ // remove any reserved functions from the options object
+ // set Request instance to be readable and writable
+ // call init
+
+ var self = this
+
+ // start with HAR, then override with additional options
+ if (options.har) {
+ self._har = new Har(self)
+ options = self._har.options(options)
+ }
+
+ stream.Stream.call(self)
+ var reserved = Object.keys(Request.prototype)
+ var nonReserved = filterForNonReserved(reserved, options)
+
+ stream.Stream.call(self)
+ util._extend(self, nonReserved)
+ options = filterOutReservedFunctions(reserved, options)
+
+ self.readable = true
+ self.writable = true
+ if (options.method) {
+ self.explicitMethod = true
+ }
+ self._auth = new Auth(self)
+ self._oauth = new OAuth(self)
+ self._multipart = new Multipart(self)
+ self._redirect = new Redirect(self)
+ self.init(options)
+}
+
+util.inherits(Request, stream.Stream)
+
+// Debugging
+Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG)
+function debug() {
+ if (Request.debug) {
+ console.error('REQUEST %s', util.format.apply(util, arguments))
+ }
+}
+
+Request.prototype.setupTunnel = function () {
+ var self = this
+
+ if (typeof self.proxy === 'string') {
+ self.proxy = url.parse(self.proxy)
+ }
+
+ if (!self.proxy || !self.tunnel) {
+ return false
+ }
+
+ // Setup Proxy Header Exclusive List and White List
+ self.proxyHeaderExclusiveList = self.proxyHeaderExclusiveList || []
+ self.proxyHeaderWhiteList = self.proxyHeaderWhiteList || defaultProxyHeaderWhiteList
+ var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)
+ var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)
+
+ // Setup Proxy Headers and Proxy Headers Host
+ // Only send the Proxy White Listed Header names
+ self.proxyHeaders = constructProxyHeaderWhiteList(self.headers, proxyHeaderWhiteList)
+ self.proxyHeaders.host = constructProxyHost(self.uri)
+ proxyHeaderExclusiveList.forEach(self.removeHeader, self)
+
+ // Set Agent from Tunnel Data
+ var tunnelFn = getTunnelFn(self)
+ var tunnelOptions = constructTunnelOptions(self)
+ self.agent = tunnelFn(tunnelOptions)
+
+ return true
+}
+
+Request.prototype.init = function (options) {
+ // init() contains all the code to setup the request object.
+ // the actual outgoing request is not started until start() is called
+ // this function is called from both the constructor and on redirect.
+ var self = this
+ if (!options) {
+ options = {}
+ }
+ self.headers = self.headers ? copy(self.headers) : {}
+
+ // Delete headers with value undefined since they break
+ // ClientRequest.OutgoingMessage.setHeader in node 0.12
+ for (var headerName in self.headers) {
+ if (typeof self.headers[headerName] === 'undefined') {
+ delete self.headers[headerName]
+ }
+ }
+
+ caseless.httpify(self, self.headers)
+
+ if (!self.method) {
+ self.method = options.method || 'GET'
+ }
+ if (!self.localAddress) {
+ self.localAddress = options.localAddress
+ }
+
+ if (!self.qsLib) {
+ self.qsLib = (options.useQuerystring ? querystring : qs)
+ }
+ if (!self.qsParseOptions) {
+ self.qsParseOptions = options.qsParseOptions
+ }
+ if (!self.qsStringifyOptions) {
+ self.qsStringifyOptions = options.qsStringifyOptions
+ }
+
+ debug(options)
+ if (!self.pool && self.pool !== false) {
+ self.pool = globalPool
+ }
+ self.dests = self.dests || []
+ self.__isRequestRequest = true
+
+ // Protect against double callback
+ if (!self._callback && self.callback) {
+ self._callback = self.callback
+ self.callback = function () {
+ if (self._callbackCalled) {
+ return // Print a warning maybe?
+ }
+ self._callbackCalled = true
+ self._callback.apply(self, arguments)
+ }
+ self.on('error', self.callback.bind())
+ self.on('complete', self.callback.bind(self, null))
+ }
+
+ // People use this property instead all the time, so support it
+ if (!self.uri && self.url) {
+ self.uri = self.url
+ delete self.url
+ }
+
+ // If there's a baseUrl, then use it as the base URL (i.e. uri must be
+ // specified as a relative path and is appended to baseUrl).
+ if (self.baseUrl) {
+ if (typeof self.baseUrl !== 'string') {
+ return self.emit('error', new Error('options.baseUrl must be a string'))
+ }
+
+ if (typeof self.uri !== 'string') {
+ return self.emit('error', new Error('options.uri must be a string when using options.baseUrl'))
+ }
+
+ if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) {
+ return self.emit('error', new Error('options.uri must be a path when using options.baseUrl'))
+ }
+
+ // Handle all cases to make sure that there's only one slash between
+ // baseUrl and uri.
+ var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1
+ var uriStartsWithSlash = self.uri.indexOf('/') === 0
+
+ if (baseUrlEndsWithSlash && uriStartsWithSlash) {
+ self.uri = self.baseUrl + self.uri.slice(1)
+ } else if (baseUrlEndsWithSlash || uriStartsWithSlash) {
+ self.uri = self.baseUrl + self.uri
+ } else if (self.uri === '') {
+ self.uri = self.baseUrl
+ } else {
+ self.uri = self.baseUrl + '/' + self.uri
+ }
+ delete self.baseUrl
+ }
+
+ // A URI is needed by this point, throw if we haven't been able to get one
+ if (!self.uri) {
+ return self.emit('error', new Error('options.uri is a required argument'))
+ }
+
+ // If a string URI/URL was given, parse it into a URL object
+ if(typeof self.uri === 'string') {
+ self.uri = url.parse(self.uri)
+ }
+
+ // DEPRECATED: Warning for users of the old Unix Sockets URL Scheme
+ if (self.uri.protocol === 'unix:') {
+ return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`'))
+ }
+
+ // Support Unix Sockets
+ if(self.uri.host === 'unix') {
+ // Get the socket & request paths from the URL
+ var unixParts = self.uri.path.split(':')
+ , host = unixParts[0]
+ , path = unixParts[1]
+ // Apply unix properties to request
+ self.socketPath = host
+ self.uri.pathname = path
+ self.uri.path = path
+ self.uri.host = host
+ self.uri.hostname = host
+ self.uri.isUnix = true
+ }
+
+ if (self.strictSSL === false) {
+ self.rejectUnauthorized = false
+ }
+
+ if(!self.hasOwnProperty('proxy')) {
+ self.proxy = getProxyFromURI(self.uri)
+ }
+
+ self.tunnel = getTunnelOption(self, options)
+ if (self.proxy) {
+ self.setupTunnel()
+ }
+
+ if (!self.uri.pathname) {self.uri.pathname = '/'}
+
+ if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) {
+ // Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar
+ // Detect and reject it as soon as possible
+ var faultyUri = url.format(self.uri)
+ var message = 'Invalid URI "' + faultyUri + '"'
+ if (Object.keys(options).length === 0) {
+ // No option ? This can be the sign of a redirect
+ // As this is a case where the user cannot do anything (they didn't call request directly with this URL)
+ // they should be warned that it can be caused by a redirection (can save some hair)
+ message += '. This can be caused by a crappy redirection.'
+ }
+ // This error was fatal
+ return self.emit('error', new Error(message))
+ }
+
+ self._redirect.onRequest()
+
+ self.setHost = false
+ if (!self.hasHeader('host')) {
+ var hostHeaderName = self.originalHostHeaderName || 'host'
+ self.setHeader(hostHeaderName, self.uri.hostname)
+ if (self.uri.port) {
+ if ( !(self.uri.port === 80 && self.uri.protocol === 'http:') &&
+ !(self.uri.port === 443 && self.uri.protocol === 'https:') ) {
+ self.setHeader(hostHeaderName, self.getHeader('host') + (':' + self.uri.port) )
+ }
+ }
+ self.setHost = true
+ }
+
+ self.jar(self._jar || options.jar)
+
+ if (!self.uri.port) {
+ if (self.uri.protocol === 'http:') {self.uri.port = 80}
+ else if (self.uri.protocol === 'https:') {self.uri.port = 443}
+ }
+
+ if (self.proxy && !self.tunnel) {
+ self.port = self.proxy.port
+ self.host = self.proxy.hostname
+ } else {
+ self.port = self.uri.port
+ self.host = self.uri.hostname
+ }
+
+ if (options.form) {
+ self.form(options.form)
+ }
+
+ if (options.formData) {
+ var formData = options.formData
+ var requestForm = self.form()
+ var appendFormValue = function (key, value) {
+ if (value.hasOwnProperty('value') && value.hasOwnProperty('options')) {
+ requestForm.append(key, value.value, value.options)
+ } else {
+ requestForm.append(key, value)
+ }
+ }
+ for (var formKey in formData) {
+ if (formData.hasOwnProperty(formKey)) {
+ var formValue = formData[formKey]
+ if (formValue instanceof Array) {
+ for (var j = 0; j < formValue.length; j++) {
+ appendFormValue(formKey, formValue[j])
+ }
+ } else {
+ appendFormValue(formKey, formValue)
+ }
+ }
+ }
+ }
+
+ if (options.qs) {
+ self.qs(options.qs)
+ }
+
+ if (self.uri.path) {
+ self.path = self.uri.path
+ } else {
+ self.path = self.uri.pathname + (self.uri.search || '')
+ }
+
+ if (self.path.length === 0) {
+ self.path = '/'
+ }
+
+ // Auth must happen last in case signing is dependent on other headers
+ if (options.oauth) {
+ self.oauth(options.oauth)
+ }
+
+ if (options.aws) {
+ self.aws(options.aws)
+ }
+
+ if (options.hawk) {
+ self.hawk(options.hawk)
+ }
+
+ if (options.httpSignature) {
+ self.httpSignature(options.httpSignature)
+ }
+
+ if (options.auth) {
+ if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) {
+ options.auth.user = options.auth.username
+ }
+ if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) {
+ options.auth.pass = options.auth.password
+ }
+
+ self.auth(
+ options.auth.user,
+ options.auth.pass,
+ options.auth.sendImmediately,
+ options.auth.bearer
+ )
+ }
+
+ if (self.gzip && !self.hasHeader('accept-encoding')) {
+ self.setHeader('accept-encoding', 'gzip')
+ }
+
+ if (self.uri.auth && !self.hasHeader('authorization')) {
+ var uriAuthPieces = self.uri.auth.split(':').map(function(item){ return querystring.unescape(item) })
+ self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true)
+ }
+
+ if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) {
+ var proxyAuthPieces = self.proxy.auth.split(':').map(function(item){
+ return querystring.unescape(item)
+ })
+ var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':'))
+ self.setHeader('proxy-authorization', authHeader)
+ }
+
+ if (self.proxy && !self.tunnel) {
+ self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
+ }
+
+ if (options.json) {
+ self.json(options.json)
+ }
+ if (options.multipart) {
+ self.multipart(options.multipart)
+ }
+
+ if (options.time) {
+ self.timing = true
+ self.elapsedTime = self.elapsedTime || 0
+ }
+
+ if (self.body) {
+ var length = 0
+ if (!Buffer.isBuffer(self.body)) {
+ if (Array.isArray(self.body)) {
+ for (var i = 0; i < self.body.length; i++) {
+ length += self.body[i].length
+ }
+ } else {
+ self.body = new Buffer(self.body)
+ length = self.body.length
+ }
+ } else {
+ length = self.body.length
+ }
+ if (length) {
+ if (!self.hasHeader('content-length')) {
+ self.setHeader('content-length', length)
+ }
+ } else {
+ throw new Error('Argument error, options.body.')
+ }
+ }
+
+ var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
+ , defaultModules = {'http:':http, 'https:':https}
+ , httpModules = self.httpModules || {}
+
+ self.httpModule = httpModules[protocol] || defaultModules[protocol]
+
+ if (!self.httpModule) {
+ return self.emit('error', new Error('Invalid protocol: ' + protocol))
+ }
+
+ if (options.ca) {
+ self.ca = options.ca
+ }
+
+ if (!self.agent) {
+ if (options.agentOptions) {
+ self.agentOptions = options.agentOptions
+ }
+
+ if (options.agentClass) {
+ self.agentClass = options.agentClass
+ } else if (options.forever) {
+ self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
+ } else {
+ self.agentClass = self.httpModule.Agent
+ }
+ }
+
+ if (self.pool === false) {
+ self.agent = false
+ } else {
+ self.agent = self.agent || self.getNewAgent()
+ }
+
+ self.on('pipe', function (src) {
+ if (self.ntick && self._started) {
+ throw new Error('You cannot pipe to this stream after the outbound request has started.')
+ }
+ self.src = src
+ if (isReadStream(src)) {
+ if (!self.hasHeader('content-type')) {
+ self.setHeader('content-type', mime.lookup(src.path))
+ }
+ } else {
+ if (src.headers) {
+ for (var i in src.headers) {
+ if (!self.hasHeader(i)) {
+ self.setHeader(i, src.headers[i])
+ }
+ }
+ }
+ if (self._json && !self.hasHeader('content-type')) {
+ self.setHeader('content-type', 'application/json')
+ }
+ if (src.method && !self.explicitMethod) {
+ self.method = src.method
+ }
+ }
+
+ // self.on('pipe', function () {
+ // console.error('You have already piped to this stream. Pipeing twice is likely to break the request.')
+ // })
+ })
+
+ defer(function () {
+ if (self._aborted) {
+ return
+ }
+
+ var end = function () {
+ if (self._form) {
+ if (!self._auth.hasAuth) {
+ self._form.pipe(self)
+ }
+ else if (self._auth.hasAuth && self._auth.sentAuth) {
+ self._form.pipe(self)
+ }
+ }
+ if (self._multipart && self._multipart.chunked) {
+ self._multipart.body.pipe(self)
+ }
+ if (self.body) {
+ if (Array.isArray(self.body)) {
+ self.body.forEach(function (part) {
+ self.write(part)
+ })
+ } else {
+ self.write(self.body)
+ }
+ self.end()
+ } else if (self.requestBodyStream) {
+ console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')
+ self.requestBodyStream.pipe(self)
+ } else if (!self.src) {
+ if (self._auth.hasAuth && !self._auth.sentAuth) {
+ self.end()
+ return
+ }
+ if (self.method !== 'GET' && typeof self.method !== 'undefined') {
+ self.setHeader('content-length', 0)
+ }
+ self.end()
+ }
+ }
+
+ if (self._form && !self.hasHeader('content-length')) {
+ // Before ending the request, we had to compute the length of the whole form, asyncly
+ self.setHeader(self._form.getHeaders())
+ self._form.getLength(function (err, length) {
+ if (!err) {
+ self.setHeader('content-length', length)
+ }
+ end()
+ })
+ } else {
+ end()
+ }
+
+ self.ntick = true
+ })
+
+}
+
+// Must call this when following a redirect from https to http or vice versa
+// Attempts to keep everything as identical as possible, but update the
+// httpModule, Tunneling agent, and/or Forever Agent in use.
+Request.prototype._updateProtocol = function () {
+ var self = this
+ var protocol = self.uri.protocol
+
+ if (protocol === 'https:' || self.tunnel) {
+ // previously was doing http, now doing https
+ // if it's https, then we might need to tunnel now.
+ if (self.proxy) {
+ if (self.setupTunnel()) {
+ return
+ }
+ }
+
+ self.httpModule = https
+ switch (self.agentClass) {
+ case ForeverAgent:
+ self.agentClass = ForeverAgent.SSL
+ break
+ case http.Agent:
+ self.agentClass = https.Agent
+ break
+ default:
+ // nothing we can do. Just hope for the best.
+ return
+ }
+
+ // if there's an agent, we need to get a new one.
+ if (self.agent) {
+ self.agent = self.getNewAgent()
+ }
+
+ } else {
+ // previously was doing https, now doing http
+ self.httpModule = http
+ switch (self.agentClass) {
+ case ForeverAgent.SSL:
+ self.agentClass = ForeverAgent
+ break
+ case https.Agent:
+ self.agentClass = http.Agent
+ break
+ default:
+ // nothing we can do. just hope for the best
+ return
+ }
+
+ // if there's an agent, then get a new one.
+ if (self.agent) {
+ self.agent = null
+ self.agent = self.getNewAgent()
+ }
+ }
+}
+
+Request.prototype.getNewAgent = function () {
+ var self = this
+ var Agent = self.agentClass
+ var options = {}
+ if (self.agentOptions) {
+ for (var i in self.agentOptions) {
+ options[i] = self.agentOptions[i]
+ }
+ }
+ if (self.ca) {
+ options.ca = self.ca
+ }
+ if (self.ciphers) {
+ options.ciphers = self.ciphers
+ }
+ if (self.secureProtocol) {
+ options.secureProtocol = self.secureProtocol
+ }
+ if (self.secureOptions) {
+ options.secureOptions = self.secureOptions
+ }
+ if (typeof self.rejectUnauthorized !== 'undefined') {
+ options.rejectUnauthorized = self.rejectUnauthorized
+ }
+
+ if (self.cert && self.key) {
+ options.key = self.key
+ options.cert = self.cert
+ }
+
+ if (self.pfx) {
+ options.pfx = self.pfx
+ }
+
+ if (self.passphrase) {
+ options.passphrase = self.passphrase
+ }
+
+ var poolKey = ''
+
+ // different types of agents are in different pools
+ if (Agent !== self.httpModule.Agent) {
+ poolKey += Agent.name
+ }
+
+ // ca option is only relevant if proxy or destination are https
+ var proxy = self.proxy
+ if (typeof proxy === 'string') {
+ proxy = url.parse(proxy)
+ }
+ var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
+
+ if (isHttps) {
+ if (options.ca) {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.ca
+ }
+
+ if (typeof options.rejectUnauthorized !== 'undefined') {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.rejectUnauthorized
+ }
+
+ if (options.cert) {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.cert.toString('ascii') + options.key.toString('ascii')
+ }
+
+ if (options.pfx) {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.pfx.toString('ascii')
+ }
+
+ if (options.ciphers) {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.ciphers
+ }
+
+ if (options.secureProtocol) {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.secureProtocol
+ }
+
+ if (options.secureOptions) {
+ if (poolKey) {
+ poolKey += ':'
+ }
+ poolKey += options.secureOptions
+ }
+ }
+
+ if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) {
+ // not doing anything special. Use the globalAgent
+ return self.httpModule.globalAgent
+ }
+
+ // we're using a stored agent. Make sure it's protocol-specific
+ poolKey = self.uri.protocol + poolKey
+
+ // generate a new agent for this setting if none yet exists
+ if (!self.pool[poolKey]) {
+ self.pool[poolKey] = new Agent(options)
+ // properly set maxSockets on new agents
+ if (self.pool.maxSockets) {
+ self.pool[poolKey].maxSockets = self.pool.maxSockets
+ }
+ }
+
+ return self.pool[poolKey]
+}
+
+Request.prototype.start = function () {
+ // start() is called once we are ready to send the outgoing HTTP request.
+ // this is usually called on the first write(), end() or on nextTick()
+ var self = this
+
+ if (self._aborted) {
+ return
+ }
+
+ self._started = true
+ self.method = self.method || 'GET'
+ self.href = self.uri.href
+
+ if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {
+ self.setHeader('content-length', self.src.stat.size)
+ }
+ if (self._aws) {
+ self.aws(self._aws, true)
+ }
+
+ // We have a method named auth, which is completely different from the http.request
+ // auth option. If we don't remove it, we're gonna have a bad time.
+ var reqOptions = copy(self)
+ delete reqOptions.auth
+
+ debug('make request', self.uri.href)
+
+ self.req = self.httpModule.request(reqOptions)
+
+ if (self.timing) {
+ self.startTime = new Date().getTime()
+ }
+
+ if (self.timeout && !self.timeoutTimer) {
+ var timeout = self.timeout < 0 ? 0 : self.timeout
+ self.timeoutTimer = setTimeout(function () {
+ self.abort()
+ var e = new Error('ETIMEDOUT')
+ e.code = 'ETIMEDOUT'
+ self.emit('error', e)
+ }, timeout)
+
+ // Set additional timeout on socket - in case if remote
+ // server freeze after sending headers
+ if (self.req.setTimeout) { // only works on node 0.6+
+ self.req.setTimeout(timeout, function () {
+ if (self.req) {
+ self.req.abort()
+ var e = new Error('ESOCKETTIMEDOUT')
+ e.code = 'ESOCKETTIMEDOUT'
+ self.emit('error', e)
+ }
+ })
+ }
+ }
+
+ self.req.on('response', self.onRequestResponse.bind(self))
+ self.req.on('error', self.onRequestError.bind(self))
+ self.req.on('drain', function() {
+ self.emit('drain')
+ })
+ self.req.on('socket', function(socket) {
+ self.emit('socket', socket)
+ })
+
+ self.on('end', function() {
+ if ( self.req.connection ) {
+ self.req.connection.removeListener('error', connectionErrorHandler)
+ }
+ })
+ self.emit('request', self.req)
+}
+
+Request.prototype.onRequestError = function (error) {
+ var self = this
+ if (self._aborted) {
+ return
+ }
+ if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET'
+ && self.agent.addRequestNoreuse) {
+ self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
+ self.start()
+ self.req.end()
+ return
+ }
+ if (self.timeout && self.timeoutTimer) {
+ clearTimeout(self.timeoutTimer)
+ self.timeoutTimer = null
+ }
+ self.emit('error', error)
+}
+
+Request.prototype.onRequestResponse = function (response) {
+ var self = this
+ debug('onRequestResponse', self.uri.href, response.statusCode, response.headers)
+ response.on('end', function() {
+ if (self.timing) {
+ self.elapsedTime += (new Date().getTime() - self.startTime)
+ debug('elapsed time', self.elapsedTime)
+ response.elapsedTime = self.elapsedTime
+ }
+ debug('response end', self.uri.href, response.statusCode, response.headers)
+ })
+
+ // The check on response.connection is a workaround for browserify.
+ if (response.connection && response.connection.listeners('error').indexOf(connectionErrorHandler) === -1) {
+ response.connection.setMaxListeners(0)
+ response.connection.once('error', connectionErrorHandler)
+ }
+ if (self._aborted) {
+ debug('aborted', self.uri.href)
+ response.resume()
+ return
+ }
+ if (self._paused) {
+ response.pause()
+ } else if (response.resume) {
+ // response.resume should be defined, but check anyway before calling. Workaround for browserify.
+ response.resume()
+ }
+
+ self.response = response
+ response.request = self
+ response.toJSON = responseToJSON
+
+ // XXX This is different on 0.10, because SSL is strict by default
+ if (self.httpModule === https &&
+ self.strictSSL && (!response.hasOwnProperty('client') ||
+ !response.client.authorized)) {
+ debug('strict ssl error', self.uri.href)
+ var sslErr = response.hasOwnProperty('client') ? response.client.authorizationError : self.uri.href + ' does not support SSL'
+ self.emit('error', new Error('SSL Error: ' + sslErr))
+ return
+ }
+
+ // Save the original host before any redirect (if it changes, we need to
+ // remove any authorization headers). Also remember the case of the header
+ // name because lots of broken servers expect Host instead of host and we
+ // want the caller to be able to specify this.
+ self.originalHost = self.getHeader('host')
+ if (!self.originalHostHeaderName) {
+ self.originalHostHeaderName = self.hasHeader('host')
+ }
+ if (self.setHost) {
+ self.removeHeader('host')
+ }
+ if (self.timeout && self.timeoutTimer) {
+ clearTimeout(self.timeoutTimer)
+ self.timeoutTimer = null
+ }
+
+ var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar
+ var addCookie = function (cookie) {
+ //set the cookie if it's domain in the href's domain.
+ try {
+ targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true})
+ } catch (e) {
+ self.emit('error', e)
+ }
+ }
+
+ response.caseless = caseless(response.headers)
+
+ if (response.caseless.has('set-cookie') && (!self._disableCookies)) {
+ var headerName = response.caseless.has('set-cookie')
+ if (Array.isArray(response.headers[headerName])) {
+ response.headers[headerName].forEach(addCookie)
+ } else {
+ addCookie(response.headers[headerName])
+ }
+ }
+
+ if (self._redirect.onResponse(response)) {
+ return // Ignore the rest of the response
+ } else {
+ // Be a good stream and emit end when the response is finished.
+ // Hack to emit end on close because of a core bug that never fires end
+ response.on('close', function () {
+ if (!self._ended) {
+ self.response.emit('end')
+ }
+ })
+
+ response.on('end', function () {
+ self._ended = true
+ })
+
+ var dataStream
+ if (self.gzip) {
+ var contentEncoding = response.headers['content-encoding'] || 'identity'
+ contentEncoding = contentEncoding.trim().toLowerCase()
+
+ if (contentEncoding === 'gzip') {
+ dataStream = zlib.createGunzip()
+ response.pipe(dataStream)
+ } else {
+ // Since previous versions didn't check for Content-Encoding header,
+ // ignore any invalid values to preserve backwards-compatibility
+ if (contentEncoding !== 'identity') {
+ debug('ignoring unrecognized Content-Encoding ' + contentEncoding)
+ }
+ dataStream = response
+ }
+ } else {
+ dataStream = response
+ }
+
+ if (self.encoding) {
+ if (self.dests.length !== 0) {
+ console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.')
+ } else if (dataStream.setEncoding) {
+ dataStream.setEncoding(self.encoding)
+ } else {
+ // Should only occur on node pre-v0.9.4 (joyent/node@9b5abe5) with
+ // zlib streams.
+ // If/When support for 0.9.4 is dropped, this should be unnecessary.
+ dataStream = dataStream.pipe(stringstream(self.encoding))
+ }
+ }
+
+ self.emit('response', response)
+
+ self.dests.forEach(function (dest) {
+ self.pipeDest(dest)
+ })
+
+ dataStream.on('data', function (chunk) {
+ self._destdata = true
+ self.emit('data', chunk)
+ })
+ dataStream.on('end', function (chunk) {
+ self.emit('end', chunk)
+ })
+ dataStream.on('error', function (error) {
+ self.emit('error', error)
+ })
+ dataStream.on('close', function () {self.emit('close')})
+
+ if (self.callback) {
+ var buffer = bl()
+ , strings = []
+
+ self.on('data', function (chunk) {
+ if (Buffer.isBuffer(chunk)) {
+ buffer.append(chunk)
+ } else {
+ strings.push(chunk)
+ }
+ })
+ self.on('end', function () {
+ debug('end event', self.uri.href)
+ if (self._aborted) {
+ debug('aborted', self.uri.href)
+ return
+ }
+
+ if (buffer.length) {
+ debug('has body', self.uri.href, buffer.length)
+ if (self.encoding === null) {
+ // response.body = buffer
+ // can't move to this until https://github.com/rvagg/bl/issues/13
+ response.body = buffer.slice()
+ } else {
+ response.body = buffer.toString(self.encoding)
+ }
+ } else if (strings.length) {
+ // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation.
+ // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse().
+ if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') {
+ strings[0] = strings[0].substring(1)
+ }
+ response.body = strings.join('')
+ }
+
+ if (self._json) {
+ try {
+ response.body = JSON.parse(response.body, self._jsonReviver)
+ } catch (e) {}
+ }
+ debug('emitting complete', self.uri.href)
+ if(typeof response.body === 'undefined' && !self._json) {
+ response.body = self.encoding === null ? new Buffer(0) : ''
+ }
+ self.emit('complete', response, response.body)
+ })
+ }
+ //if no callback
+ else{
+ self.on('end', function () {
+ if (self._aborted) {
+ debug('aborted', self.uri.href)
+ return
+ }
+ self.emit('complete', response)
+ })
+ }
+ }
+ debug('finish init function', self.uri.href)
+}
+
+Request.prototype.abort = function () {
+ var self = this
+ self._aborted = true
+
+ if (self.req) {
+ self.req.abort()
+ }
+ else if (self.response) {
+ self.response.abort()
+ }
+
+ self.emit('abort')
+}
+
+Request.prototype.pipeDest = function (dest) {
+ var self = this
+ var response = self.response
+ // Called after the response is received
+ if (dest.headers && !dest.headersSent) {
+ if (response.caseless.has('content-type')) {
+ var ctname = response.caseless.has('content-type')
+ if (dest.setHeader) {
+ dest.setHeader(ctname, response.headers[ctname])
+ }
+ else {
+ dest.headers[ctname] = response.headers[ctname]
+ }
+ }
+
+ if (response.caseless.has('content-length')) {
+ var clname = response.caseless.has('content-length')
+ if (dest.setHeader) {
+ dest.setHeader(clname, response.headers[clname])
+ } else {
+ dest.headers[clname] = response.headers[clname]
+ }
+ }
+ }
+ if (dest.setHeader && !dest.headersSent) {
+ for (var i in response.headers) {
+ // If the response content is being decoded, the Content-Encoding header
+ // of the response doesn't represent the piped content, so don't pass it.
+ if (!self.gzip || i !== 'content-encoding') {
+ dest.setHeader(i, response.headers[i])
+ }
+ }
+ dest.statusCode = response.statusCode
+ }
+ if (self.pipefilter) {
+ self.pipefilter(response, dest)
+ }
+}
+
+Request.prototype.qs = function (q, clobber) {
+ var self = this
+ var base
+ if (!clobber && self.uri.query) {
+ base = self.qsLib.parse(self.uri.query, self.qsParseOptions)
+ } else {
+ base = {}
+ }
+
+ for (var i in q) {
+ base[i] = q[i]
+ }
+
+ if (self.qsLib.stringify(base, self.qsStringifyOptions) === ''){
+ return self
+ }
+
+ var qs = self.qsLib.stringify(base, self.qsStringifyOptions)
+
+ self.uri = url.parse(self.uri.href.split('?')[0] + '?' + rfc3986(qs))
+ self.url = self.uri
+ self.path = self.uri.path
+
+ return self
+}
+Request.prototype.form = function (form) {
+ var self = this
+ if (form) {
+ self.setHeader('content-type', 'application/x-www-form-urlencoded')
+ self.body = (typeof form === 'string')
+ ? form.toString('utf8')
+ : self.qsLib.stringify(form, self.qsStringifyOptions).toString('utf8')
+ self.body = rfc3986(self.body)
+ return self
+ }
+ // create form-data object
+ self._form = new FormData()
+ self._form.on('error', function(err) {
+ err.message = 'form-data: ' + err.message
+ self.emit('error', err)
+ self.abort()
+ })
+ return self._form
+}
+Request.prototype.multipart = function (multipart) {
+ var self = this
+
+ self._multipart.onRequest(multipart)
+
+ if (!self._multipart.chunked) {
+ self.body = self._multipart.body
+ }
+
+ return self
+}
+Request.prototype.json = function (val) {
+ var self = this
+
+ if (!self.hasHeader('accept')) {
+ self.setHeader('accept', 'application/json')
+ }
+
+ self._json = true
+ if (typeof val === 'boolean') {
+ if (self.body !== undefined) {
+ if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
+ self.body = safeStringify(self.body)
+ } else {
+ self.body = rfc3986(self.body)
+ }
+ if (!self.hasHeader('content-type')) {
+ self.setHeader('content-type', 'application/json')
+ }
+ }
+ } else {
+ self.body = safeStringify(val)
+ if (!self.hasHeader('content-type')) {
+ self.setHeader('content-type', 'application/json')
+ }
+ }
+
+ if (typeof self.jsonReviver === 'function') {
+ self._jsonReviver = self.jsonReviver
+ }
+
+ return self
+}
+Request.prototype.getHeader = function (name, headers) {
+ var self = this
+ var result, re, match
+ if (!headers) {
+ headers = self.headers
+ }
+ Object.keys(headers).forEach(function (key) {
+ if (key.length !== name.length) {
+ return
+ }
+ re = new RegExp(name, 'i')
+ match = key.match(re)
+ if (match) {
+ result = headers[key]
+ }
+ })
+ return result
+}
+
+Request.prototype.auth = function (user, pass, sendImmediately, bearer) {
+ var self = this
+
+ self._auth.onRequest(user, pass, sendImmediately, bearer)
+
+ return self
+}
+Request.prototype.aws = function (opts, now) {
+ var self = this
+
+ if (!now) {
+ self._aws = opts
+ return self
+ }
+ var date = new Date()
+ self.setHeader('date', date.toUTCString())
+ var auth =
+ { key: opts.key
+ , secret: opts.secret
+ , verb: self.method.toUpperCase()
+ , date: date
+ , contentType: self.getHeader('content-type') || ''
+ , md5: self.getHeader('content-md5') || ''
+ , amazonHeaders: aws.canonicalizeHeaders(self.headers)
+ }
+ var path = self.uri.path
+ if (opts.bucket && path) {
+ auth.resource = '/' + opts.bucket + path
+ } else if (opts.bucket && !path) {
+ auth.resource = '/' + opts.bucket
+ } else if (!opts.bucket && path) {
+ auth.resource = path
+ } else if (!opts.bucket && !path) {
+ auth.resource = '/'
+ }
+ auth.resource = aws.canonicalizeResource(auth.resource)
+ self.setHeader('authorization', aws.authorization(auth))
+
+ return self
+}
+Request.prototype.httpSignature = function (opts) {
+ var self = this
+ httpSignature.signRequest({
+ getHeader: function(header) {
+ return self.getHeader(header, self.headers)
+ },
+ setHeader: function(header, value) {
+ self.setHeader(header, value)
+ },
+ method: self.method,
+ path: self.path
+ }, opts)
+ debug('httpSignature authorization', self.getHeader('authorization'))
+
+ return self
+}
+Request.prototype.hawk = function (opts) {
+ var self = this
+ self.setHeader('Authorization', hawk.client.header(self.uri, self.method, opts).field)
+}
+Request.prototype.oauth = function (_oauth) {
+ var self = this
+
+ self._oauth.onRequest(_oauth)
+
+ return self
+}
+
+Request.prototype.jar = function (jar) {
+ var self = this
+ var cookies
+
+ if (self._redirect.redirectsFollowed === 0) {
+ self.originalCookieHeader = self.getHeader('cookie')
+ }
+
+ if (!jar) {
+ // disable cookies
+ cookies = false
+ self._disableCookies = true
+ } else {
+ var targetCookieJar = (jar && jar.getCookieString) ? jar : globalCookieJar
+ var urihref = self.uri.href
+ //fetch cookie in the Specified host
+ if (targetCookieJar) {
+ cookies = targetCookieJar.getCookieString(urihref)
+ }
+ }
+
+ //if need cookie and cookie is not empty
+ if (cookies && cookies.length) {
+ if (self.originalCookieHeader) {
+ // Don't overwrite existing Cookie header
+ self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies)
+ } else {
+ self.setHeader('cookie', cookies)
+ }
+ }
+ self._jar = jar
+ return self
+}
+
+
+// Stream API
+Request.prototype.pipe = function (dest, opts) {
+ var self = this
+
+ if (self.response) {
+ if (self._destdata) {
+ throw new Error('You cannot pipe after data has been emitted from the response.')
+ } else if (self._ended) {
+ throw new Error('You cannot pipe after the response has been ended.')
+ } else {
+ stream.Stream.prototype.pipe.call(self, dest, opts)
+ self.pipeDest(dest)
+ return dest
+ }
+ } else {
+ self.dests.push(dest)
+ stream.Stream.prototype.pipe.call(self, dest, opts)
+ return dest
+ }
+}
+Request.prototype.write = function () {
+ var self = this
+ if (!self._started) {
+ self.start()
+ }
+ return self.req.write.apply(self.req, arguments)
+}
+Request.prototype.end = function (chunk) {
+ var self = this
+ if (chunk) {
+ self.write(chunk)
+ }
+ if (!self._started) {
+ self.start()
+ }
+ self.req.end()
+}
+Request.prototype.pause = function () {
+ var self = this
+ if (!self.response) {
+ self._paused = true
+ } else {
+ self.response.pause.apply(self.response, arguments)
+ }
+}
+Request.prototype.resume = function () {
+ var self = this
+ if (!self.response) {
+ self._paused = false
+ } else {
+ self.response.resume.apply(self.response, arguments)
+ }
+}
+Request.prototype.destroy = function () {
+ var self = this
+ if (!self._ended) {
+ self.end()
+ } else if (self.response) {
+ self.response.destroy()
+ }
+}
+
+Request.defaultProxyHeaderWhiteList =
+ defaultProxyHeaderWhiteList.slice()
+
+Request.defaultProxyHeaderExclusiveList =
+ defaultProxyHeaderExclusiveList.slice()
+
+// Exports
+
+Request.prototype.toJSON = requestToJSON
+module.exports = Request
--- /dev/null
+dist/*
+node_modules/*
--- /dev/null
+language: node_js
+node_js:
+ - 0.8
+ - "0.10"
\ No newline at end of file
--- /dev/null
+# Change Log
+
+## 0.2.0
+
+* Support for consuming "indexed" source maps which do not have any remote
+ sections. See pull request #127. This introduces a minor backwards
+ incompatibility if you are monkey patching `SourceMapConsumer.prototype`
+ methods.
+
+## 0.1.43
+
+* Performance improvements for `SourceMapGenerator` and `SourceNode`. See issue
+ #148 for some discussion and issues #150, #151, and #152 for implementations.
+
+## 0.1.42
+
+* Fix an issue where `SourceNode`s from different versions of the source-map
+ library couldn't be used in conjunction with each other. See issue #142.
+
+## 0.1.41
+
+* Fix a bug with getting the source content of relative sources with a "./"
+ prefix. See issue #145 and [Bug 1090768](bugzil.la/1090768).
+
+* Add the `SourceMapConsumer.prototype.computeColumnSpans` method to compute the
+ column span of each mapping.
+
+* Add the `SourceMapConsumer.prototype.allGeneratedPositionsFor` method to find
+ all generated positions associated with a given original source and line.
+
+## 0.1.40
+
+* Performance improvements for parsing source maps in SourceMapConsumer.
+
+## 0.1.39
+
+* Fix a bug where setting a source's contents to null before any source content
+ had been set before threw a TypeError. See issue #131.
+
+## 0.1.38
+
+* Fix a bug where finding relative paths from an empty path were creating
+ absolute paths. See issue #129.
+
+## 0.1.37
+
+* Fix a bug where if the source root was an empty string, relative source paths
+ would turn into absolute source paths. Issue #124.
+
+## 0.1.36
+
+* Allow the `names` mapping property to be an empty string. Issue #121.
+
+## 0.1.35
+
+* A third optional parameter was added to `SourceNode.fromStringWithSourceMap`
+ to specify a path that relative sources in the second parameter should be
+ relative to. Issue #105.
+
+* If no file property is given to a `SourceMapGenerator`, then the resulting
+ source map will no longer have a `null` file property. The property will
+ simply not exist. Issue #104.
+
+* Fixed a bug where consecutive newlines were ignored in `SourceNode`s.
+ Issue #116.
+
+## 0.1.34
+
+* Make `SourceNode` work with windows style ("\r\n") newlines. Issue #103.
+
+* Fix bug involving source contents and the
+ `SourceMapGenerator.prototype.applySourceMap`. Issue #100.
+
+## 0.1.33
+
+* Fix some edge cases surrounding path joining and URL resolution.
+
+* Add a third parameter for relative path to
+ `SourceMapGenerator.prototype.applySourceMap`.
+
+* Fix issues with mappings and EOLs.
+
+## 0.1.32
+
+* Fixed a bug where SourceMapConsumer couldn't handle negative relative columns
+ (issue 92).
+
+* Fixed test runner to actually report number of failed tests as its process
+ exit code.
+
+* Fixed a typo when reporting bad mappings (issue 87).
+
+## 0.1.31
+
+* Delay parsing the mappings in SourceMapConsumer until queried for a source
+ location.
+
+* Support Sass source maps (which at the time of writing deviate from the spec
+ in small ways) in SourceMapConsumer.
+
+## 0.1.30
+
+* Do not join source root with a source, when the source is a data URI.
+
+* Extend the test runner to allow running single specific test files at a time.
+
+* Performance improvements in `SourceNode.prototype.walk` and
+ `SourceMapConsumer.prototype.eachMapping`.
+
+* Source map browser builds will now work inside Workers.
+
+* Better error messages when attempting to add an invalid mapping to a
+ `SourceMapGenerator`.
+
+## 0.1.29
+
+* Allow duplicate entries in the `names` and `sources` arrays of source maps
+ (usually from TypeScript) we are parsing. Fixes github issue 72.
+
+## 0.1.28
+
+* Skip duplicate mappings when creating source maps from SourceNode; github
+ issue 75.
+
+## 0.1.27
+
+* Don't throw an error when the `file` property is missing in SourceMapConsumer,
+ we don't use it anyway.
+
+## 0.1.26
+
+* Fix SourceNode.fromStringWithSourceMap for empty maps. Fixes github issue 70.
+
+## 0.1.25
+
+* Make compatible with browserify
+
+## 0.1.24
+
+* Fix issue with absolute paths and `file://` URIs. See
+ https://bugzilla.mozilla.org/show_bug.cgi?id=885597
+
+## 0.1.23
+
+* Fix issue with absolute paths and sourcesContent, github issue 64.
+
+## 0.1.22
+
+* Ignore duplicate mappings in SourceMapGenerator. Fixes github issue 21.
+
+## 0.1.21
+
+* Fixed handling of sources that start with a slash so that they are relative to
+ the source root's host.
+
+## 0.1.20
+
+* Fixed github issue #43: absolute URLs aren't joined with the source root
+ anymore.
+
+## 0.1.19
+
+* Using Travis CI to run tests.
+
+## 0.1.18
+
+* Fixed a bug in the handling of sourceRoot.
+
+## 0.1.17
+
+* Added SourceNode.fromStringWithSourceMap.
+
+## 0.1.16
+
+* Added missing documentation.
+
+* Fixed the generating of empty mappings in SourceNode.
+
+## 0.1.15
+
+* Added SourceMapGenerator.applySourceMap.
+
+## 0.1.14
+
+* The sourceRoot is now handled consistently.
+
+## 0.1.13
+
+* Added SourceMapGenerator.fromSourceMap.
+
+## 0.1.12
+
+* SourceNode now generates empty mappings too.
+
+## 0.1.11
+
+* Added name support to SourceNode.
+
+## 0.1.10
+
+* Added sourcesContent support to the customer and generator.
--- /dev/null
+
+Copyright (c) 2009-2011, Mozilla Foundation and contributors
+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 names of the Mozilla Foundation nor the names of project
+ 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+var path = require('path');
+var fs = require('fs');
+var copy = require('dryice').copy;
+
+function removeAmdefine(src) {
+ src = String(src).replace(
+ /if\s*\(typeof\s*define\s*!==\s*'function'\)\s*{\s*var\s*define\s*=\s*require\('amdefine'\)\(module,\s*require\);\s*}\s*/g,
+ '');
+ src = src.replace(
+ /\b(define\(.*)('amdefine',?)/gm,
+ '$1');
+ return src;
+}
+removeAmdefine.onRead = true;
+
+function makeNonRelative(src) {
+ return src
+ .replace(/require\('.\//g, 'require(\'source-map/')
+ .replace(/\.\.\/\.\.\/lib\//g, '');
+}
+makeNonRelative.onRead = true;
+
+function buildBrowser() {
+ console.log('\nCreating dist/source-map.js');
+
+ var project = copy.createCommonJsProject({
+ roots: [ path.join(__dirname, 'lib') ]
+ });
+
+ copy({
+ source: [
+ 'build/mini-require.js',
+ {
+ project: project,
+ require: [ 'source-map/source-map-generator',
+ 'source-map/source-map-consumer',
+ 'source-map/source-node']
+ },
+ 'build/suffix-browser.js'
+ ],
+ filter: [
+ copy.filter.moduleDefines,
+ removeAmdefine
+ ],
+ dest: 'dist/source-map.js'
+ });
+}
+
+function buildBrowserMin() {
+ console.log('\nCreating dist/source-map.min.js');
+
+ copy({
+ source: 'dist/source-map.js',
+ filter: copy.filter.uglifyjs,
+ dest: 'dist/source-map.min.js'
+ });
+}
+
+function buildFirefox() {
+ console.log('\nCreating dist/SourceMap.jsm');
+
+ var project = copy.createCommonJsProject({
+ roots: [ path.join(__dirname, 'lib') ]
+ });
+
+ copy({
+ source: [
+ 'build/prefix-source-map.jsm',
+ {
+ project: project,
+ require: [ 'source-map/source-map-consumer',
+ 'source-map/source-map-generator',
+ 'source-map/source-node' ]
+ },
+ 'build/suffix-source-map.jsm'
+ ],
+ filter: [
+ copy.filter.moduleDefines,
+ removeAmdefine,
+ makeNonRelative
+ ],
+ dest: 'dist/SourceMap.jsm'
+ });
+
+ // Create dist/test/Utils.jsm
+ console.log('\nCreating dist/test/Utils.jsm');
+
+ project = copy.createCommonJsProject({
+ roots: [ __dirname, path.join(__dirname, 'lib') ]
+ });
+
+ copy({
+ source: [
+ 'build/prefix-utils.jsm',
+ 'build/assert-shim.js',
+ {
+ project: project,
+ require: [ 'test/source-map/util' ]
+ },
+ 'build/suffix-utils.jsm'
+ ],
+ filter: [
+ copy.filter.moduleDefines,
+ removeAmdefine,
+ makeNonRelative
+ ],
+ dest: 'dist/test/Utils.jsm'
+ });
+
+ function isTestFile(f) {
+ return /^test\-.*?\.js/.test(f);
+ }
+
+ var testFiles = fs.readdirSync(path.join(__dirname, 'test', 'source-map')).filter(isTestFile);
+
+ testFiles.forEach(function (testFile) {
+ console.log('\nCreating', path.join('dist', 'test', testFile.replace(/\-/g, '_')));
+
+ copy({
+ source: [
+ 'build/test-prefix.js',
+ path.join('test', 'source-map', testFile),
+ 'build/test-suffix.js'
+ ],
+ filter: [
+ removeAmdefine,
+ makeNonRelative,
+ function (input, source) {
+ return input.replace('define(',
+ 'define("'
+ + path.join('test', 'source-map', testFile.replace(/\.js$/, ''))
+ + '", ["require", "exports", "module"], ');
+ },
+ function (input, source) {
+ return input.replace('{THIS_MODULE}', function () {
+ return "test/source-map/" + testFile.replace(/\.js$/, '');
+ });
+ }
+ ],
+ dest: path.join('dist', 'test', testFile.replace(/\-/g, '_'))
+ });
+ });
+}
+
+function ensureDir(name) {
+ var dirExists = false;
+ try {
+ dirExists = fs.statSync(name).isDirectory();
+ } catch (err) {}
+
+ if (!dirExists) {
+ fs.mkdirSync(name, 0777);
+ }
+}
+
+ensureDir("dist");
+ensureDir("dist/test");
+buildFirefox();
+buildBrowser();
+buildBrowserMin();
--- /dev/null
+# Source Map
+
+This is a library to generate and consume the source map format
+[described here][format].
+
+This library is written in the Asynchronous Module Definition format, and works
+in the following environments:
+
+* Modern Browsers supporting ECMAScript 5 (either after the build, or with an
+ AMD loader such as RequireJS)
+
+* Inside Firefox (as a JSM file, after the build)
+
+* With NodeJS versions 0.8.X and higher
+
+## Node
+
+ $ npm install source-map
+
+## Building from Source (for everywhere else)
+
+Install Node and then run
+
+ $ git clone https://fitzgen@github.com/mozilla/source-map.git
+ $ cd source-map
+ $ npm link .
+
+Next, run
+
+ $ node Makefile.dryice.js
+
+This should spew a bunch of stuff to stdout, and create the following files:
+
+* `dist/source-map.js` - The unminified browser version.
+
+* `dist/source-map.min.js` - The minified browser version.
+
+* `dist/SourceMap.jsm` - The JavaScript Module for inclusion in Firefox source.
+
+## Examples
+
+### Consuming a source map
+
+ var rawSourceMap = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ sourceRoot: 'http://example.com/www/js/',
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+
+ var smc = new SourceMapConsumer(rawSourceMap);
+
+ console.log(smc.sources);
+ // [ 'http://example.com/www/js/one.js',
+ // 'http://example.com/www/js/two.js' ]
+
+ console.log(smc.originalPositionFor({
+ line: 2,
+ column: 28
+ }));
+ // { source: 'http://example.com/www/js/two.js',
+ // line: 2,
+ // column: 10,
+ // name: 'n' }
+
+ console.log(smc.generatedPositionFor({
+ source: 'http://example.com/www/js/two.js',
+ line: 2,
+ column: 10
+ }));
+ // { line: 2, column: 28 }
+
+ smc.eachMapping(function (m) {
+ // ...
+ });
+
+### Generating a source map
+
+In depth guide:
+[**Compiling to JavaScript, and Debugging with Source Maps**](https://hacks.mozilla.org/2013/05/compiling-to-javascript-and-debugging-with-source-maps/)
+
+#### With SourceNode (high level API)
+
+ function compile(ast) {
+ switch (ast.type) {
+ case 'BinaryExpression':
+ return new SourceNode(
+ ast.location.line,
+ ast.location.column,
+ ast.location.source,
+ [compile(ast.left), " + ", compile(ast.right)]
+ );
+ case 'Literal':
+ return new SourceNode(
+ ast.location.line,
+ ast.location.column,
+ ast.location.source,
+ String(ast.value)
+ );
+ // ...
+ default:
+ throw new Error("Bad AST");
+ }
+ }
+
+ var ast = parse("40 + 2", "add.js");
+ console.log(compile(ast).toStringWithSourceMap({
+ file: 'add.js'
+ }));
+ // { code: '40 + 2',
+ // map: [object SourceMapGenerator] }
+
+#### With SourceMapGenerator (low level API)
+
+ var map = new SourceMapGenerator({
+ file: "source-mapped.js"
+ });
+
+ map.addMapping({
+ generated: {
+ line: 10,
+ column: 35
+ },
+ source: "foo.js",
+ original: {
+ line: 33,
+ column: 2
+ },
+ name: "christopher"
+ });
+
+ console.log(map.toString());
+ // '{"version":3,"file":"source-mapped.js","sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'
+
+## API
+
+Get a reference to the module:
+
+ // NodeJS
+ var sourceMap = require('source-map');
+
+ // Browser builds
+ var sourceMap = window.sourceMap;
+
+ // Inside Firefox
+ let sourceMap = {};
+ Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+
+### SourceMapConsumer
+
+A SourceMapConsumer instance represents a parsed source map which we can query
+for information about the original file positions by giving it a file position
+in the generated source.
+
+#### new SourceMapConsumer(rawSourceMap)
+
+The only parameter is the raw source map (either as a string which can be
+`JSON.parse`'d, or an object). According to the spec, source maps have the
+following attributes:
+
+* `version`: Which version of the source map spec this map is following.
+
+* `sources`: An array of URLs to the original source files.
+
+* `names`: An array of identifiers which can be referrenced by individual
+ mappings.
+
+* `sourceRoot`: Optional. The URL root from which all sources are relative.
+
+* `sourcesContent`: Optional. An array of contents of the original source files.
+
+* `mappings`: A string of base64 VLQs which contain the actual mappings.
+
+* `file`: Optional. The generated filename this source map is associated with.
+
+#### SourceMapConsumer.prototype.computeColumnSpans()
+
+Compute the last column for each generated mapping. The last column is
+inclusive.
+
+#### SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
+
+Returns the original source, line, and column information for the generated
+source's line and column positions provided. The only argument is an object with
+the following properties:
+
+* `line`: The line number in the generated source.
+
+* `column`: The column number in the generated source.
+
+and an object is returned with the following properties:
+
+* `source`: The original source file, or null if this information is not
+ available.
+
+* `line`: The line number in the original source, or null if this information is
+ not available.
+
+* `column`: The column number in the original source, or null or null if this
+ information is not available.
+
+* `name`: The original identifier, or null if this information is not available.
+
+#### SourceMapConsumer.prototype.generatedPositionFor(originalPosition)
+
+Returns the generated line and column information for the original source,
+line, and column positions provided. The only argument is an object with
+the following properties:
+
+* `source`: The filename of the original source.
+
+* `line`: The line number in the original source.
+
+* `column`: The column number in the original source.
+
+and an object is returned with the following properties:
+
+* `line`: The line number in the generated source, or null.
+
+* `column`: The column number in the generated source, or null.
+
+#### SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition)
+
+Returns all generated line and column information for the original source
+and line provided. The only argument is an object with the following
+properties:
+
+* `source`: The filename of the original source.
+
+* `line`: The line number in the original source.
+
+and an array of objects is returned, each with the following properties:
+
+* `line`: The line number in the generated source, or null.
+
+* `column`: The column number in the generated source, or null.
+
+#### SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing])
+
+Returns the original source content for the source provided. The only
+argument is the URL of the original source file.
+
+If the source content for the given source is not found, then an error is
+thrown. Optionally, pass `true` as the second param to have `null` returned
+instead.
+
+#### SourceMapConsumer.prototype.eachMapping(callback, context, order)
+
+Iterate over each mapping between an original source/line/column and a
+generated line/column in this source map.
+
+* `callback`: The function that is called with each mapping. Mappings have the
+ form `{ source, generatedLine, generatedColumn, originalLine, originalColumn,
+ name }`
+
+* `context`: Optional. If specified, this object will be the value of `this`
+ every time that `callback` is called.
+
+* `order`: Either `SourceMapConsumer.GENERATED_ORDER` or
+ `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to iterate over
+ the mappings sorted by the generated file's line/column order or the
+ original's source/line/column order, respectively. Defaults to
+ `SourceMapConsumer.GENERATED_ORDER`.
+
+### SourceMapGenerator
+
+An instance of the SourceMapGenerator represents a source map which is being
+built incrementally.
+
+#### new SourceMapGenerator([startOfSourceMap])
+
+You may pass an object with the following properties:
+
+* `file`: The filename of the generated source that this source map is
+ associated with.
+
+* `sourceRoot`: A root for all relative URLs in this source map.
+
+* `skipValidation`: Optional. When `true`, disables validation of mappings as
+ they are added. This can improve performance but should be used with
+ discretion, as a last resort. Even then, one should avoid using this flag when
+ running tests, if possible.
+
+#### SourceMapGenerator.fromSourceMap(sourceMapConsumer)
+
+Creates a new SourceMapGenerator based on a SourceMapConsumer
+
+* `sourceMapConsumer` The SourceMap.
+
+#### SourceMapGenerator.prototype.addMapping(mapping)
+
+Add a single mapping from original source line and column to the generated
+source's line and column for this source map being created. The mapping object
+should have the following properties:
+
+* `generated`: An object with the generated line and column positions.
+
+* `original`: An object with the original line and column positions.
+
+* `source`: The original source file (relative to the sourceRoot).
+
+* `name`: An optional original token name for this mapping.
+
+#### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for an original source file.
+
+* `sourceFile` the URL of the original source file.
+
+* `sourceContent` the content of the source file.
+
+#### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])
+
+Applies a SourceMap for a source file to the SourceMap.
+Each mapping to the supplied source file is rewritten using the
+supplied SourceMap. Note: The resolution for the resulting mappings
+is the minimium of this map and the supplied map.
+
+* `sourceMapConsumer`: The SourceMap to be applied.
+
+* `sourceFile`: Optional. The filename of the source file.
+ If omitted, sourceMapConsumer.file will be used, if it exists.
+ Otherwise an error will be thrown.
+
+* `sourceMapPath`: Optional. The dirname of the path to the SourceMap
+ to be applied. If relative, it is relative to the SourceMap.
+
+ This parameter is needed when the two SourceMaps aren't in the same
+ directory, and the SourceMap to be applied contains relative source
+ paths. If so, those relative source paths need to be rewritten
+ relative to the SourceMap.
+
+ If omitted, it is assumed that both SourceMaps are in the same directory,
+ thus not needing any rewriting. (Supplying `'.'` has the same effect.)
+
+#### SourceMapGenerator.prototype.toString()
+
+Renders the source map being generated to a string.
+
+### SourceNode
+
+SourceNodes provide a way to abstract over interpolating and/or concatenating
+snippets of generated JavaScript source code, while maintaining the line and
+column information associated between those snippets and the original source
+code. This is useful as the final intermediate representation a compiler might
+use before outputting the generated JS and source map.
+
+#### new SourceNode([line, column, source[, chunk[, name]]])
+
+* `line`: The original line number associated with this source node, or null if
+ it isn't associated with an original line.
+
+* `column`: The original column number associated with this source node, or null
+ if it isn't associated with an original column.
+
+* `source`: The original source's filename; null if no filename is provided.
+
+* `chunk`: Optional. Is immediately passed to `SourceNode.prototype.add`, see
+ below.
+
+* `name`: Optional. The original identifier.
+
+#### SourceNode.fromStringWithSourceMap(code, sourceMapConsumer[, relativePath])
+
+Creates a SourceNode from generated code and a SourceMapConsumer.
+
+* `code`: The generated code
+
+* `sourceMapConsumer` The SourceMap for the generated code
+
+* `relativePath` The optional path that relative sources in `sourceMapConsumer`
+ should be relative to.
+
+#### SourceNode.prototype.add(chunk)
+
+Add a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+ `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.prepend(chunk)
+
+Prepend a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+ `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for a source file. This will be added to the
+`SourceMap` in the `sourcesContent` field.
+
+* `sourceFile`: The filename of the source file
+
+* `sourceContent`: The content of the source file
+
+#### SourceNode.prototype.walk(fn)
+
+Walk over the tree of JS snippets in this node and its children. The walking
+function is called once for each snippet of JS and is passed that snippet and
+the its original associated source's line/column location.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.walkSourceContents(fn)
+
+Walk over the tree of SourceNodes. The walking function is called for each
+source file content and is passed the filename and source content.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.join(sep)
+
+Like `Array.prototype.join` except for SourceNodes. Inserts the separator
+between each of this source node's children.
+
+* `sep`: The separator.
+
+#### SourceNode.prototype.replaceRight(pattern, replacement)
+
+Call `String.prototype.replace` on the very right-most source snippet. Useful
+for trimming whitespace from the end of a source node, etc.
+
+* `pattern`: The pattern to replace.
+
+* `replacement`: The thing to replace the pattern with.
+
+#### SourceNode.prototype.toString()
+
+Return the string representation of this source node. Walks over the tree and
+concatenates all the various snippets together to one string.
+
+#### SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])
+
+Returns the string representation of this tree of source nodes, plus a
+SourceMapGenerator which contains all the mappings between the generated and
+original sources.
+
+The arguments are the same as those to `new SourceMapGenerator`.
+
+## Tests
+
+[](https://travis-ci.org/mozilla/source-map)
+
+Install NodeJS version 0.8.0 or greater, then run `node test/run-tests.js`.
+
+To add new tests, create a new file named `test/test-<your new test name>.js`
+and export your test functions with names that start with "test", for example
+
+ exports["test doing the foo bar"] = function (assert, util) {
+ ...
+ };
+
+The new test will be located automatically when you run the suite.
+
+The `util` argument is the test utility module located at `test/source-map/util`.
+
+The `assert` argument is a cut down version of node's assert module. You have
+access to the following assertion functions:
+
+* `doesNotThrow`
+
+* `equal`
+
+* `ok`
+
+* `strictEqual`
+
+* `throws`
+
+(The reason for the restricted set of test functions is because we need the
+tests to run inside Firefox's test suite as well and so the assert module is
+shimmed in that environment. See `build/assert-shim.js`.)
+
+[format]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+[feature]: https://wiki.mozilla.org/DevTools/Features/SourceMap
+[Dryice]: https://github.com/mozilla/dryice
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('test/source-map/assert', ['exports'], function (exports) {
+
+ let do_throw = function (msg) {
+ throw new Error(msg);
+ };
+
+ exports.init = function (throw_fn) {
+ do_throw = throw_fn;
+ };
+
+ exports.doesNotThrow = function (fn) {
+ try {
+ fn();
+ }
+ catch (e) {
+ do_throw(e.message);
+ }
+ };
+
+ exports.equal = function (actual, expected, msg) {
+ msg = msg || String(actual) + ' != ' + String(expected);
+ if (actual != expected) {
+ do_throw(msg);
+ }
+ };
+
+ exports.ok = function (val, msg) {
+ msg = msg || String(val) + ' is falsey';
+ if (!Boolean(val)) {
+ do_throw(msg);
+ }
+ };
+
+ exports.strictEqual = function (actual, expected, msg) {
+ msg = msg || String(actual) + ' !== ' + String(expected);
+ if (actual !== expected) {
+ do_throw(msg);
+ }
+ };
+
+ exports.throws = function (fn) {
+ try {
+ fn();
+ do_throw('Expected an error to be thrown, but it wasn\'t.');
+ }
+ catch (e) {
+ }
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/**
+ * Define a module along with a payload.
+ * @param {string} moduleName Name for the payload
+ * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
+ * @param {function} payload Function with (require, exports, module) params
+ */
+function define(moduleName, deps, payload) {
+ if (typeof moduleName != "string") {
+ throw new TypeError('Expected string, got: ' + moduleName);
+ }
+
+ if (arguments.length == 2) {
+ payload = deps;
+ }
+
+ if (moduleName in define.modules) {
+ throw new Error("Module already defined: " + moduleName);
+ }
+ define.modules[moduleName] = payload;
+};
+
+/**
+ * The global store of un-instantiated modules
+ */
+define.modules = {};
+
+
+/**
+ * We invoke require() in the context of a Domain so we can have multiple
+ * sets of modules running separate from each other.
+ * This contrasts with JSMs which are singletons, Domains allows us to
+ * optionally load a CommonJS module twice with separate data each time.
+ * Perhaps you want 2 command lines with a different set of commands in each,
+ * for example.
+ */
+function Domain() {
+ this.modules = {};
+ this._currentModule = null;
+}
+
+(function () {
+
+ /**
+ * Lookup module names and resolve them by calling the definition function if
+ * needed.
+ * There are 2 ways to call this, either with an array of dependencies and a
+ * callback to call when the dependencies are found (which can happen
+ * asynchronously in an in-page context) or with a single string an no callback
+ * where the dependency is resolved synchronously and returned.
+ * The API is designed to be compatible with the CommonJS AMD spec and
+ * RequireJS.
+ * @param {string[]|string} deps A name, or names for the payload
+ * @param {function|undefined} callback Function to call when the dependencies
+ * are resolved
+ * @return {undefined|object} The module required or undefined for
+ * array/callback method
+ */
+ Domain.prototype.require = function(deps, callback) {
+ if (Array.isArray(deps)) {
+ var params = deps.map(function(dep) {
+ return this.lookup(dep);
+ }, this);
+ if (callback) {
+ callback.apply(null, params);
+ }
+ return undefined;
+ }
+ else {
+ return this.lookup(deps);
+ }
+ };
+
+ function normalize(path) {
+ var bits = path.split('/');
+ var i = 1;
+ while (i < bits.length) {
+ if (bits[i] === '..') {
+ bits.splice(i-1, 1);
+ } else if (bits[i] === '.') {
+ bits.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ return bits.join('/');
+ }
+
+ function join(a, b) {
+ a = a.trim();
+ b = b.trim();
+ if (/^\//.test(b)) {
+ return b;
+ } else {
+ return a.replace(/\/*$/, '/') + b;
+ }
+ }
+
+ function dirname(path) {
+ var bits = path.split('/');
+ bits.pop();
+ return bits.join('/');
+ }
+
+ /**
+ * Lookup module names and resolve them by calling the definition function if
+ * needed.
+ * @param {string} moduleName A name for the payload to lookup
+ * @return {object} The module specified by aModuleName or null if not found.
+ */
+ Domain.prototype.lookup = function(moduleName) {
+ if (/^\./.test(moduleName)) {
+ moduleName = normalize(join(dirname(this._currentModule), moduleName));
+ }
+
+ if (moduleName in this.modules) {
+ var module = this.modules[moduleName];
+ return module;
+ }
+
+ if (!(moduleName in define.modules)) {
+ throw new Error("Module not defined: " + moduleName);
+ }
+
+ var module = define.modules[moduleName];
+
+ if (typeof module == "function") {
+ var exports = {};
+ var previousModule = this._currentModule;
+ this._currentModule = moduleName;
+ module(this.require.bind(this), exports, { id: moduleName, uri: "" });
+ this._currentModule = previousModule;
+ module = exports;
+ }
+
+ // cache the resulting module object for next time
+ this.modules[moduleName] = module;
+
+ return module;
+ };
+
+}());
+
+define.Domain = Domain;
+define.globalDomain = new Domain();
+var require = define.globalDomain.require.bind(define.globalDomain);
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+this.EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
+Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
+
+this.EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.sourceMap = {
+ SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer,
+ SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator,
+ SourceNode: require('source-map/source-node').SourceNode
+};
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+this.SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+this.SourceNode = require('source-map/source-node').SourceNode;
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+function runSourceMapTests(modName, do_throw) {
+ let mod = require(modName);
+ let assert = require('test/source-map/assert');
+ let util = require('test/source-map/util');
+
+ assert.init(do_throw);
+
+ for (let k in mod) {
+ if (/^test/.test(k)) {
+ mod[k](assert, util);
+ }
+ }
+
+}
+this.runSourceMapTests = runSourceMapTests;
--- /dev/null
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://test/Utils.jsm');
--- /dev/null
+function run_test() {
+ runSourceMapTests('{THIS_MODULE}', do_throw);
+}
--- /dev/null
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
+exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
+exports.SourceNode = require('./source-map/source-node').SourceNode;
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+
+ /**
+ * A data structure which is a combination of an array and a set. Adding a new
+ * member is O(1), testing for membership is O(1), and finding the index of an
+ * element is O(1). Removing elements from the set is not supported. Only
+ * strings are supported for membership.
+ */
+ function ArraySet() {
+ this._array = [];
+ this._set = {};
+ }
+
+ /**
+ * Static method for creating ArraySet instances from an existing array.
+ */
+ ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
+ var set = new ArraySet();
+ for (var i = 0, len = aArray.length; i < len; i++) {
+ set.add(aArray[i], aAllowDuplicates);
+ }
+ return set;
+ };
+
+ /**
+ * Add the given string to this set.
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
+ var isDuplicate = this.has(aStr);
+ var idx = this._array.length;
+ if (!isDuplicate || aAllowDuplicates) {
+ this._array.push(aStr);
+ }
+ if (!isDuplicate) {
+ this._set[util.toSetString(aStr)] = idx;
+ }
+ };
+
+ /**
+ * Is the given string a member of this set?
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.has = function ArraySet_has(aStr) {
+ return Object.prototype.hasOwnProperty.call(this._set,
+ util.toSetString(aStr));
+ };
+
+ /**
+ * What is the index of the given string in the array?
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+ if (this.has(aStr)) {
+ return this._set[util.toSetString(aStr)];
+ }
+ throw new Error('"' + aStr + '" is not in the set.');
+ };
+
+ /**
+ * What is the element at the given index?
+ *
+ * @param Number aIdx
+ */
+ ArraySet.prototype.at = function ArraySet_at(aIdx) {
+ if (aIdx >= 0 && aIdx < this._array.length) {
+ return this._array[aIdx];
+ }
+ throw new Error('No element indexed by ' + aIdx);
+ };
+
+ /**
+ * Returns the array representation of this set (which has the proper indices
+ * indicated by indexOf). Note that this is a copy of the internal array used
+ * for storing the members so that no one can mess with internal state.
+ */
+ ArraySet.prototype.toArray = function ArraySet_toArray() {
+ return this._array.slice();
+ };
+
+ exports.ArraySet = ArraySet;
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. 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 Google 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var base64 = require('./base64');
+
+ // A single base 64 digit can contain 6 bits of data. For the base 64 variable
+ // length quantities we use in the source map spec, the first bit is the sign,
+ // the next four bits are the actual value, and the 6th bit is the
+ // continuation bit. The continuation bit tells us whether there are more
+ // digits in this value following this digit.
+ //
+ // Continuation
+ // | Sign
+ // | |
+ // V V
+ // 101011
+
+ var VLQ_BASE_SHIFT = 5;
+
+ // binary: 100000
+ var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+ // binary: 011111
+ var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+ // binary: 100000
+ var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+ /**
+ * Converts from a two-complement value to a value where the sign bit is
+ * placed in the least significant bit. For example, as decimals:
+ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+ * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+ */
+ function toVLQSigned(aValue) {
+ return aValue < 0
+ ? ((-aValue) << 1) + 1
+ : (aValue << 1) + 0;
+ }
+
+ /**
+ * Converts to a two-complement value from a value where the sign bit is
+ * placed in the least significant bit. For example, as decimals:
+ * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+ * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+ */
+ function fromVLQSigned(aValue) {
+ var isNegative = (aValue & 1) === 1;
+ var shifted = aValue >> 1;
+ return isNegative
+ ? -shifted
+ : shifted;
+ }
+
+ /**
+ * Returns the base 64 VLQ encoded value.
+ */
+ exports.encode = function base64VLQ_encode(aValue) {
+ var encoded = "";
+ var digit;
+
+ var vlq = toVLQSigned(aValue);
+
+ do {
+ digit = vlq & VLQ_BASE_MASK;
+ vlq >>>= VLQ_BASE_SHIFT;
+ if (vlq > 0) {
+ // There are still more digits in this value, so we must make sure the
+ // continuation bit is marked.
+ digit |= VLQ_CONTINUATION_BIT;
+ }
+ encoded += base64.encode(digit);
+ } while (vlq > 0);
+
+ return encoded;
+ };
+
+ /**
+ * Decodes the next base 64 VLQ value from the given string and returns the
+ * value and the rest of the string via the out parameter.
+ */
+ exports.decode = function base64VLQ_decode(aStr, aOutParam) {
+ var i = 0;
+ var strLen = aStr.length;
+ var result = 0;
+ var shift = 0;
+ var continuation, digit;
+
+ do {
+ if (i >= strLen) {
+ throw new Error("Expected more digits in base 64 VLQ value.");
+ }
+ digit = base64.decode(aStr.charAt(i++));
+ continuation = !!(digit & VLQ_CONTINUATION_BIT);
+ digit &= VLQ_BASE_MASK;
+ result = result + (digit << shift);
+ shift += VLQ_BASE_SHIFT;
+ } while (continuation);
+
+ aOutParam.value = fromVLQSigned(result);
+ aOutParam.rest = aStr.slice(i);
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var charToIntMap = {};
+ var intToCharMap = {};
+
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ .split('')
+ .forEach(function (ch, index) {
+ charToIntMap[ch] = index;
+ intToCharMap[index] = ch;
+ });
+
+ /**
+ * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+ */
+ exports.encode = function base64_encode(aNumber) {
+ if (aNumber in intToCharMap) {
+ return intToCharMap[aNumber];
+ }
+ throw new TypeError("Must be between 0 and 63: " + aNumber);
+ };
+
+ /**
+ * Decode a single base 64 digit to an integer.
+ */
+ exports.decode = function base64_decode(aChar) {
+ if (aChar in charToIntMap) {
+ return charToIntMap[aChar];
+ }
+ throw new TypeError("Not a valid base 64 digit: " + aChar);
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+ var binarySearch = require('./binary-search');
+ var ArraySet = require('./array-set').ArraySet;
+ var base64VLQ = require('./base64-vlq');
+ var SourceMapConsumer = require('./source-map-consumer').SourceMapConsumer;
+
+ /**
+ * A BasicSourceMapConsumer instance represents a parsed source map which we can
+ * query for information about the original file positions by giving it a file
+ * position in the generated source.
+ *
+ * The only parameter is the raw source map (either as a JSON string, or
+ * already parsed to an object). According to the spec, source maps have the
+ * following attributes:
+ *
+ * - version: Which version of the source map spec this map is following.
+ * - sources: An array of URLs to the original source files.
+ * - names: An array of identifiers which can be referrenced by individual mappings.
+ * - sourceRoot: Optional. The URL root from which all sources are relative.
+ * - sourcesContent: Optional. An array of contents of the original source files.
+ * - mappings: A string of base64 VLQs which contain the actual mappings.
+ * - file: Optional. The generated file this source map is associated with.
+ *
+ * Here is an example source map, taken from the source map spec[0]:
+ *
+ * {
+ * version : 3,
+ * file: "out.js",
+ * sourceRoot : "",
+ * sources: ["foo.js", "bar.js"],
+ * names: ["src", "maps", "are", "fun"],
+ * mappings: "AA,AB;;ABCDE;"
+ * }
+ *
+ * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+ */
+ function BasicSourceMapConsumer(aSourceMap) {
+ var sourceMap = aSourceMap;
+ if (typeof aSourceMap === 'string') {
+ sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+ }
+
+ var version = util.getArg(sourceMap, 'version');
+ var sources = util.getArg(sourceMap, 'sources');
+ // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
+ // requires the array) to play nice here.
+ var names = util.getArg(sourceMap, 'names', []);
+ var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+ var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
+ var mappings = util.getArg(sourceMap, 'mappings');
+ var file = util.getArg(sourceMap, 'file', null);
+
+ // Once again, Sass deviates from the spec and supplies the version as a
+ // string rather than a number, so we use loose equality checking here.
+ if (version != this._version) {
+ throw new Error('Unsupported version: ' + version);
+ }
+
+ // Some source maps produce relative source paths like "./foo.js" instead of
+ // "foo.js". Normalize these first so that future comparisons will succeed.
+ // See bugzil.la/1090768.
+ sources = sources.map(util.normalize);
+
+ // Pass `true` below to allow duplicate names and sources. While source maps
+ // are intended to be compressed and deduplicated, the TypeScript compiler
+ // sometimes generates source maps with duplicates in them. See Github issue
+ // #72 and bugzil.la/889492.
+ this._names = ArraySet.fromArray(names, true);
+ this._sources = ArraySet.fromArray(sources, true);
+
+ this.sourceRoot = sourceRoot;
+ this.sourcesContent = sourcesContent;
+ this._mappings = mappings;
+ this.file = file;
+ }
+
+ BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
+ BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
+
+ /**
+ * Create a BasicSourceMapConsumer from a SourceMapGenerator.
+ *
+ * @param SourceMapGenerator aSourceMap
+ * The source map that will be consumed.
+ * @returns BasicSourceMapConsumer
+ */
+ BasicSourceMapConsumer.fromSourceMap =
+ function SourceMapConsumer_fromSourceMap(aSourceMap) {
+ var smc = Object.create(BasicSourceMapConsumer.prototype);
+
+ smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
+ smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
+ smc.sourceRoot = aSourceMap._sourceRoot;
+ smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
+ smc.sourceRoot);
+ smc.file = aSourceMap._file;
+
+ smc.__generatedMappings = aSourceMap._mappings.toArray().slice();
+ smc.__originalMappings = aSourceMap._mappings.toArray().slice()
+ .sort(util.compareByOriginalPositions);
+
+ return smc;
+ };
+
+ /**
+ * The version of the source mapping spec that we are consuming.
+ */
+ BasicSourceMapConsumer.prototype._version = 3;
+
+ /**
+ * The list of original sources.
+ */
+ Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
+ get: function () {
+ return this._sources.toArray().map(function (s) {
+ return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+ }, this);
+ }
+ });
+
+ /**
+ * Parse the mappings in a string in to a data structure which we can easily
+ * query (the ordered arrays in the `this.__generatedMappings` and
+ * `this.__originalMappings` properties).
+ */
+ BasicSourceMapConsumer.prototype._parseMappings =
+ function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+ var generatedLine = 1;
+ var previousGeneratedColumn = 0;
+ var previousOriginalLine = 0;
+ var previousOriginalColumn = 0;
+ var previousSource = 0;
+ var previousName = 0;
+ var str = aStr;
+ var temp = {};
+ var mapping;
+
+ while (str.length > 0) {
+ if (str.charAt(0) === ';') {
+ generatedLine++;
+ str = str.slice(1);
+ previousGeneratedColumn = 0;
+ }
+ else if (str.charAt(0) === ',') {
+ str = str.slice(1);
+ }
+ else {
+ mapping = {};
+ mapping.generatedLine = generatedLine;
+
+ // Generated column.
+ base64VLQ.decode(str, temp);
+ mapping.generatedColumn = previousGeneratedColumn + temp.value;
+ previousGeneratedColumn = mapping.generatedColumn;
+ str = temp.rest;
+
+ if (str.length > 0 && !this._nextCharIsMappingSeparator(str)) {
+ // Original source.
+ base64VLQ.decode(str, temp);
+ mapping.source = this._sources.at(previousSource + temp.value);
+ previousSource += temp.value;
+ str = temp.rest;
+ if (str.length === 0 || this._nextCharIsMappingSeparator(str)) {
+ throw new Error('Found a source, but no line and column');
+ }
+
+ // Original line.
+ base64VLQ.decode(str, temp);
+ mapping.originalLine = previousOriginalLine + temp.value;
+ previousOriginalLine = mapping.originalLine;
+ // Lines are stored 0-based
+ mapping.originalLine += 1;
+ str = temp.rest;
+ if (str.length === 0 || this._nextCharIsMappingSeparator(str)) {
+ throw new Error('Found a source and line, but no column');
+ }
+
+ // Original column.
+ base64VLQ.decode(str, temp);
+ mapping.originalColumn = previousOriginalColumn + temp.value;
+ previousOriginalColumn = mapping.originalColumn;
+ str = temp.rest;
+
+ if (str.length > 0 && !this._nextCharIsMappingSeparator(str)) {
+ // Original name.
+ base64VLQ.decode(str, temp);
+ mapping.name = this._names.at(previousName + temp.value);
+ previousName += temp.value;
+ str = temp.rest;
+ }
+ }
+
+ this.__generatedMappings.push(mapping);
+ if (typeof mapping.originalLine === 'number') {
+ this.__originalMappings.push(mapping);
+ }
+ }
+ }
+
+ this.__generatedMappings.sort(util.compareByGeneratedPositions);
+ this.__originalMappings.sort(util.compareByOriginalPositions);
+ };
+
+ /**
+ * Find the mapping that best matches the hypothetical "needle" mapping that
+ * we are searching for in the given "haystack" of mappings.
+ */
+ BasicSourceMapConsumer.prototype._findMapping =
+ function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
+ aColumnName, aComparator) {
+ // To return the position we are searching for, we must first find the
+ // mapping for the given position and then return the opposite position it
+ // points to. Because the mappings are sorted, we can use binary search to
+ // find the best mapping.
+
+ if (aNeedle[aLineName] <= 0) {
+ throw new TypeError('Line must be greater than or equal to 1, got '
+ + aNeedle[aLineName]);
+ }
+ if (aNeedle[aColumnName] < 0) {
+ throw new TypeError('Column must be greater than or equal to 0, got '
+ + aNeedle[aColumnName]);
+ }
+
+ return binarySearch.search(aNeedle, aMappings, aComparator);
+ };
+
+ /**
+ * Compute the last column for each generated mapping. The last column is
+ * inclusive.
+ */
+ BasicSourceMapConsumer.prototype.computeColumnSpans =
+ function SourceMapConsumer_computeColumnSpans() {
+ for (var index = 0; index < this._generatedMappings.length; ++index) {
+ var mapping = this._generatedMappings[index];
+
+ // Mappings do not contain a field for the last generated columnt. We
+ // can come up with an optimistic estimate, however, by assuming that
+ // mappings are contiguous (i.e. given two consecutive mappings, the
+ // first mapping ends where the second one starts).
+ if (index + 1 < this._generatedMappings.length) {
+ var nextMapping = this._generatedMappings[index + 1];
+
+ if (mapping.generatedLine === nextMapping.generatedLine) {
+ mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
+ continue;
+ }
+ }
+
+ // The last mapping for each line spans the entire line.
+ mapping.lastGeneratedColumn = Infinity;
+ }
+ };
+
+ /**
+ * Returns the original source, line, and column information for the generated
+ * source's line and column positions provided. The only argument is an object
+ * with the following properties:
+ *
+ * - line: The line number in the generated source.
+ * - column: The column number in the generated source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - source: The original source file, or null.
+ * - line: The line number in the original source, or null.
+ * - column: The column number in the original source, or null.
+ * - name: The original identifier, or null.
+ */
+ BasicSourceMapConsumer.prototype.originalPositionFor =
+ function SourceMapConsumer_originalPositionFor(aArgs) {
+ var needle = {
+ generatedLine: util.getArg(aArgs, 'line'),
+ generatedColumn: util.getArg(aArgs, 'column')
+ };
+
+ var index = this._findMapping(needle,
+ this._generatedMappings,
+ "generatedLine",
+ "generatedColumn",
+ util.compareByGeneratedPositions);
+
+ if (index >= 0) {
+ var mapping = this._generatedMappings[index];
+
+ if (mapping.generatedLine === needle.generatedLine) {
+ var source = util.getArg(mapping, 'source', null);
+ if (source != null && this.sourceRoot != null) {
+ source = util.join(this.sourceRoot, source);
+ }
+ return {
+ source: source,
+ line: util.getArg(mapping, 'originalLine', null),
+ column: util.getArg(mapping, 'originalColumn', null),
+ name: util.getArg(mapping, 'name', null)
+ };
+ }
+ }
+
+ return {
+ source: null,
+ line: null,
+ column: null,
+ name: null
+ };
+ };
+
+ /**
+ * Returns the original source content. The only argument is the url of the
+ * original source file. Returns null if no original source content is
+ * availible.
+ */
+ BasicSourceMapConsumer.prototype.sourceContentFor =
+ function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
+ if (!this.sourcesContent) {
+ return null;
+ }
+
+ if (this.sourceRoot != null) {
+ aSource = util.relative(this.sourceRoot, aSource);
+ }
+
+ if (this._sources.has(aSource)) {
+ return this.sourcesContent[this._sources.indexOf(aSource)];
+ }
+
+ var url;
+ if (this.sourceRoot != null
+ && (url = util.urlParse(this.sourceRoot))) {
+ // XXX: file:// URIs and absolute paths lead to unexpected behavior for
+ // many users. We can help them out when they expect file:// URIs to
+ // behave like it would if they were running a local HTTP server. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
+ var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+ if (url.scheme == "file"
+ && this._sources.has(fileUriAbsPath)) {
+ return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
+ }
+
+ if ((!url.path || url.path == "/")
+ && this._sources.has("/" + aSource)) {
+ return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+ }
+ }
+
+ // This function is used recursively from
+ // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we
+ // don't want to throw if we can't find the source - we just want to
+ // return null, so we provide a flag to exit gracefully.
+ if (nullOnMissing) {
+ return null;
+ }
+ else {
+ throw new Error('"' + aSource + '" is not in the SourceMap.');
+ }
+ };
+
+ /**
+ * Returns the generated line and column information for the original source,
+ * line, and column positions provided. The only argument is an object with
+ * the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source.
+ * - column: The column number in the original source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - line: The line number in the generated source, or null.
+ * - column: The column number in the generated source, or null.
+ */
+ BasicSourceMapConsumer.prototype.generatedPositionFor =
+ function SourceMapConsumer_generatedPositionFor(aArgs) {
+ var needle = {
+ source: util.getArg(aArgs, 'source'),
+ originalLine: util.getArg(aArgs, 'line'),
+ originalColumn: util.getArg(aArgs, 'column')
+ };
+
+ if (this.sourceRoot != null) {
+ needle.source = util.relative(this.sourceRoot, needle.source);
+ }
+
+ var index = this._findMapping(needle,
+ this._originalMappings,
+ "originalLine",
+ "originalColumn",
+ util.compareByOriginalPositions);
+
+ if (index >= 0) {
+ var mapping = this._originalMappings[index];
+
+ return {
+ line: util.getArg(mapping, 'generatedLine', null),
+ column: util.getArg(mapping, 'generatedColumn', null),
+ lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+ };
+ }
+
+ return {
+ line: null,
+ column: null,
+ lastColumn: null
+ };
+ };
+
+ exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ /**
+ * Recursive implementation of binary search.
+ *
+ * @param aLow Indices here and lower do not contain the needle.
+ * @param aHigh Indices here and higher do not contain the needle.
+ * @param aNeedle The element being searched for.
+ * @param aHaystack The non-empty array being searched.
+ * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+ */
+ function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
+ // This function terminates when one of the following is true:
+ //
+ // 1. We find the exact element we are looking for.
+ //
+ // 2. We did not find the exact element, but we can return the index of
+ // the next closest element that is less than that element.
+ //
+ // 3. We did not find the exact element, and there is no next-closest
+ // element which is less than the one we are searching for, so we
+ // return -1.
+ var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+ var cmp = aCompare(aNeedle, aHaystack[mid], true);
+ if (cmp === 0) {
+ // Found the element we are looking for.
+ return mid;
+ }
+ else if (cmp > 0) {
+ // aHaystack[mid] is greater than our needle.
+ if (aHigh - mid > 1) {
+ // The element is in the upper half.
+ return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
+ }
+ // We did not find an exact match, return the next closest one
+ // (termination case 2).
+ return mid;
+ }
+ else {
+ // aHaystack[mid] is less than our needle.
+ if (mid - aLow > 1) {
+ // The element is in the lower half.
+ return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
+ }
+ // The exact needle element was not found in this haystack. Determine if
+ // we are in termination case (2) or (3) and return the appropriate thing.
+ return aLow < 0 ? -1 : aLow;
+ }
+ }
+
+ /**
+ * This is an implementation of binary search which will always try and return
+ * the index of next lowest value checked if there is no exact hit. This is
+ * because mappings between original and generated line/col pairs are single
+ * points, and there is an implicit region between each of them, so a miss
+ * just means that you aren't on the very start of a region.
+ *
+ * @param aNeedle The element you are looking for.
+ * @param aHaystack The array that is being searched.
+ * @param aCompare A function which takes the needle and an element in the
+ * array and returns -1, 0, or 1 depending on whether the needle is less
+ * than, equal to, or greater than the element, respectively.
+ */
+ exports.search = function search(aNeedle, aHaystack, aCompare) {
+ if (aHaystack.length === 0) {
+ return -1;
+ }
+ return recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+ var binarySearch = require('./binary-search');
+ var SourceMapConsumer = require('./source-map-consumer').SourceMapConsumer;
+ var BasicSourceMapConsumer = require('./basic-source-map-consumer').BasicSourceMapConsumer;
+
+ /**
+ * An IndexedSourceMapConsumer instance represents a parsed source map which
+ * we can query for information. It differs from BasicSourceMapConsumer in
+ * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
+ * input.
+ *
+ * The only parameter is a raw source map (either as a JSON string, or already
+ * parsed to an object). According to the spec for indexed source maps, they
+ * have the following attributes:
+ *
+ * - version: Which version of the source map spec this map is following.
+ * - file: Optional. The generated file this source map is associated with.
+ * - sections: A list of section definitions.
+ *
+ * Each value under the "sections" field has two fields:
+ * - offset: The offset into the original specified at which this section
+ * begins to apply, defined as an object with a "line" and "column"
+ * field.
+ * - map: A source map definition. This source map could also be indexed,
+ * but doesn't have to be.
+ *
+ * Instead of the "map" field, it's also possible to have a "url" field
+ * specifying a URL to retrieve a source map from, but that's currently
+ * unsupported.
+ *
+ * Here's an example source map, taken from the source map spec[0], but
+ * modified to omit a section which uses the "url" field.
+ *
+ * {
+ * version : 3,
+ * file: "app.js",
+ * sections: [{
+ * offset: {line:100, column:10},
+ * map: {
+ * version : 3,
+ * file: "section.js",
+ * sources: ["foo.js", "bar.js"],
+ * names: ["src", "maps", "are", "fun"],
+ * mappings: "AAAA,E;;ABCDE;"
+ * }
+ * }],
+ * }
+ *
+ * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
+ */
+ function IndexedSourceMapConsumer(aSourceMap) {
+ var sourceMap = aSourceMap;
+ if (typeof aSourceMap === 'string') {
+ sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+ }
+
+ var version = util.getArg(sourceMap, 'version');
+ var sections = util.getArg(sourceMap, 'sections');
+
+ if (version != this._version) {
+ throw new Error('Unsupported version: ' + version);
+ }
+
+ var lastOffset = {
+ line: -1,
+ column: 0
+ };
+ this._sections = sections.map(function (s) {
+ if (s.url) {
+ // The url field will require support for asynchronicity.
+ // See https://github.com/mozilla/source-map/issues/16
+ throw new Error('Support for url field in sections not implemented.');
+ }
+ var offset = util.getArg(s, 'offset');
+ var offsetLine = util.getArg(offset, 'line');
+ var offsetColumn = util.getArg(offset, 'column');
+
+ if (offsetLine < lastOffset.line ||
+ (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) {
+ throw new Error('Section offsets must be ordered and non-overlapping.');
+ }
+ lastOffset = offset;
+
+ return {
+ generatedOffset: {
+ // The offset fields are 0-based, but we use 1-based indices when
+ // encoding/decoding from VLQ.
+ generatedLine: offsetLine + 1,
+ generatedColumn: offsetColumn + 1
+ },
+ consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+ }
+ });
+ }
+
+ IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
+ IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;
+
+ /**
+ * The version of the source mapping spec that we are consuming.
+ */
+ IndexedSourceMapConsumer.prototype._version = 3;
+
+ /**
+ * The list of original sources.
+ */
+ Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
+ get: function () {
+ var sources = [];
+ for (var i = 0; i < this._sections.length; i++) {
+ for (var j = 0; j < this._sections[i].consumer.sources.length; j++) {
+ sources.push(this._sections[i].consumer.sources[j]);
+ }
+ };
+ return sources;
+ }
+ });
+
+ /**
+ * Returns the original source, line, and column information for the generated
+ * source's line and column positions provided. The only argument is an object
+ * with the following properties:
+ *
+ * - line: The line number in the generated source.
+ * - column: The column number in the generated source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - source: The original source file, or null.
+ * - line: The line number in the original source, or null.
+ * - column: The column number in the original source, or null.
+ * - name: The original identifier, or null.
+ */
+ IndexedSourceMapConsumer.prototype.originalPositionFor =
+ function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
+ var needle = {
+ generatedLine: util.getArg(aArgs, 'line'),
+ generatedColumn: util.getArg(aArgs, 'column')
+ };
+
+ // Find the section containing the generated position we're trying to map
+ // to an original position.
+ var sectionIndex = binarySearch.search(needle, this._sections,
+ function(needle, section) {
+ var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ return (needle.generatedColumn -
+ section.generatedOffset.generatedColumn);
+ });
+ var section = this._sections[sectionIndex];
+
+ if (!section) {
+ return {
+ source: null,
+ line: null,
+ column: null,
+ name: null
+ };
+ }
+
+ return section.consumer.originalPositionFor({
+ line: needle.generatedLine -
+ (section.generatedOffset.generatedLine - 1),
+ column: needle.generatedColumn -
+ (section.generatedOffset.generatedLine === needle.generatedLine
+ ? section.generatedOffset.generatedColumn - 1
+ : 0)
+ });
+ };
+
+ /**
+ * Returns the original source content. The only argument is the url of the
+ * original source file. Returns null if no original source content is
+ * available.
+ */
+ IndexedSourceMapConsumer.prototype.sourceContentFor =
+ function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
+ for (var i = 0; i < this._sections.length; i++) {
+ var section = this._sections[i];
+
+ var content = section.consumer.sourceContentFor(aSource, true);
+ if (content) {
+ return content;
+ }
+ }
+ if (nullOnMissing) {
+ return null;
+ }
+ else {
+ throw new Error('"' + aSource + '" is not in the SourceMap.');
+ }
+ };
+
+ /**
+ * Returns the generated line and column information for the original source,
+ * line, and column positions provided. The only argument is an object with
+ * the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source.
+ * - column: The column number in the original source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - line: The line number in the generated source, or null.
+ * - column: The column number in the generated source, or null.
+ */
+ IndexedSourceMapConsumer.prototype.generatedPositionFor =
+ function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
+ for (var i = 0; i < this._sections.length; i++) {
+ var section = this._sections[i];
+
+ // Only consider this section if the requested source is in the list of
+ // sources of the consumer.
+ if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) {
+ continue;
+ }
+ var generatedPosition = section.consumer.generatedPositionFor(aArgs);
+ if (generatedPosition) {
+ var ret = {
+ line: generatedPosition.line +
+ (section.generatedOffset.generatedLine - 1),
+ column: generatedPosition.column +
+ (section.generatedOffset.generatedLine === generatedPosition.line
+ ? section.generatedOffset.generatedColumn - 1
+ : 0)
+ };
+ return ret;
+ }
+ }
+
+ return {
+ line: null,
+ column: null
+ };
+ };
+
+ /**
+ * Parse the mappings in a string in to a data structure which we can easily
+ * query (the ordered arrays in the `this.__generatedMappings` and
+ * `this.__originalMappings` properties).
+ */
+ IndexedSourceMapConsumer.prototype._parseMappings =
+ function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+ this.__generatedMappings = [];
+ this.__originalMappings = [];
+ for (var i = 0; i < this._sections.length; i++) {
+ var section = this._sections[i];
+ var sectionMappings = section.consumer._generatedMappings;
+ for (var j = 0; j < sectionMappings.length; j++) {
+ var mapping = sectionMappings[i];
+
+ var source = mapping.source;
+ var sourceRoot = section.consumer.sourceRoot;
+
+ if (source != null && sourceRoot != null) {
+ source = util.join(sourceRoot, source);
+ }
+
+ // The mappings coming from the consumer for the section have
+ // generated positions relative to the start of the section, so we
+ // need to offset them to be relative to the start of the concatenated
+ // generated file.
+ var adjustedMapping = {
+ source: source,
+ generatedLine: mapping.generatedLine +
+ (section.generatedOffset.generatedLine - 1),
+ generatedColumn: mapping.column +
+ (section.generatedOffset.generatedLine === mapping.generatedLine)
+ ? section.generatedOffset.generatedColumn - 1
+ : 0,
+ originalLine: mapping.originalLine,
+ originalColumn: mapping.originalColumn,
+ name: mapping.name
+ };
+
+ this.__generatedMappings.push(adjustedMapping);
+ if (typeof adjustedMapping.originalLine === 'number') {
+ this.__originalMappings.push(adjustedMapping);
+ }
+ };
+ };
+
+ this.__generatedMappings.sort(util.compareByGeneratedPositions);
+ this.__originalMappings.sort(util.compareByOriginalPositions);
+ };
+
+ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+
+ /**
+ * Determine whether mappingB is after mappingA with respect to generated
+ * position.
+ */
+ function generatedPositionAfter(mappingA, mappingB) {
+ // Optimized for most common case
+ var lineA = mappingA.generatedLine;
+ var lineB = mappingB.generatedLine;
+ var columnA = mappingA.generatedColumn;
+ var columnB = mappingB.generatedColumn;
+ return lineB > lineA || lineB == lineA && columnB >= columnA ||
+ util.compareByGeneratedPositions(mappingA, mappingB) <= 0;
+ }
+
+ /**
+ * A data structure to provide a sorted view of accumulated mappings in a
+ * performance conscious manner. It trades a neglibable overhead in general
+ * case for a large speedup in case of mappings being added in order.
+ */
+ function MappingList() {
+ this._array = [];
+ this._sorted = true;
+ // Serves as infimum
+ this._last = {generatedLine: -1, generatedColumn: 0};
+ }
+
+ /**
+ * Iterate through internal items. This method takes the same arguments that
+ * `Array.prototype.forEach` takes.
+ *
+ * NOTE: The order of the mappings is NOT guaranteed.
+ */
+ MappingList.prototype.unsortedForEach =
+ function MappingList_forEach(aCallback, aThisArg) {
+ this._array.forEach(aCallback, aThisArg);
+ };
+
+ /**
+ * Add the given source mapping.
+ *
+ * @param Object aMapping
+ */
+ MappingList.prototype.add = function MappingList_add(aMapping) {
+ var mapping;
+ if (generatedPositionAfter(this._last, aMapping)) {
+ this._last = aMapping;
+ this._array.push(aMapping);
+ } else {
+ this._sorted = false;
+ this._array.push(aMapping);
+ }
+ };
+
+ /**
+ * Returns the flat, sorted array of mappings. The mappings are sorted by
+ * generated position.
+ *
+ * WARNING: This method returns internal data without copying, for
+ * performance. The return value must NOT be mutated, and should be treated as
+ * an immutable borrow. If you want to take ownership, you must make your own
+ * copy.
+ */
+ MappingList.prototype.toArray = function MappingList_toArray() {
+ if (!this._sorted) {
+ this._array.sort(util.compareByGeneratedPositions);
+ this._sorted = true;
+ }
+ return this._array;
+ };
+
+ exports.MappingList = MappingList;
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+
+ function SourceMapConsumer(aSourceMap) {
+ var sourceMap = aSourceMap;
+ if (typeof aSourceMap === 'string') {
+ sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+ }
+
+ // We do late requires because the subclasses require() this file.
+ if (sourceMap.sections != null) {
+ var indexedSourceMapConsumer = require('./indexed-source-map-consumer');
+ return new indexedSourceMapConsumer.IndexedSourceMapConsumer(sourceMap);
+ } else {
+ var basicSourceMapConsumer = require('./basic-source-map-consumer');
+ return new basicSourceMapConsumer.BasicSourceMapConsumer(sourceMap);
+ }
+ }
+
+ SourceMapConsumer.fromSourceMap = function(aSourceMap) {
+ var basicSourceMapConsumer = require('./basic-source-map-consumer');
+ return basicSourceMapConsumer.BasicSourceMapConsumer
+ .fromSourceMap(aSourceMap);
+ }
+
+ /**
+ * The version of the source mapping spec that we are consuming.
+ */
+ SourceMapConsumer.prototype._version = 3;
+
+
+ // `__generatedMappings` and `__originalMappings` are arrays that hold the
+ // parsed mapping coordinates from the source map's "mappings" attribute. They
+ // are lazily instantiated, accessed via the `_generatedMappings` and
+ // `_originalMappings` getters respectively, and we only parse the mappings
+ // and create these arrays once queried for a source location. We jump through
+ // these hoops because there can be many thousands of mappings, and parsing
+ // them is expensive, so we only want to do it if we must.
+ //
+ // Each object in the arrays is of the form:
+ //
+ // {
+ // generatedLine: The line number in the generated code,
+ // generatedColumn: The column number in the generated code,
+ // source: The path to the original source file that generated this
+ // chunk of code,
+ // originalLine: The line number in the original source that
+ // corresponds to this chunk of generated code,
+ // originalColumn: The column number in the original source that
+ // corresponds to this chunk of generated code,
+ // name: The name of the original symbol which generated this chunk of
+ // code.
+ // }
+ //
+ // All properties except for `generatedLine` and `generatedColumn` can be
+ // `null`.
+ //
+ // `_generatedMappings` is ordered by the generated positions.
+ //
+ // `_originalMappings` is ordered by the original positions.
+
+ SourceMapConsumer.prototype.__generatedMappings = null;
+ Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+ get: function () {
+ if (!this.__generatedMappings) {
+ this.__generatedMappings = [];
+ this.__originalMappings = [];
+ this._parseMappings(this._mappings, this.sourceRoot);
+ }
+
+ return this.__generatedMappings;
+ }
+ });
+
+ SourceMapConsumer.prototype.__originalMappings = null;
+ Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+ get: function () {
+ if (!this.__originalMappings) {
+ this.__generatedMappings = [];
+ this.__originalMappings = [];
+ this._parseMappings(this._mappings, this.sourceRoot);
+ }
+
+ return this.__originalMappings;
+ }
+ });
+
+ SourceMapConsumer.prototype._nextCharIsMappingSeparator =
+ function SourceMapConsumer_nextCharIsMappingSeparator(aStr) {
+ var c = aStr.charAt(0);
+ return c === ";" || c === ",";
+ };
+
+ /**
+ * Parse the mappings in a string in to a data structure which we can easily
+ * query (the ordered arrays in the `this.__generatedMappings` and
+ * `this.__originalMappings` properties).
+ */
+ SourceMapConsumer.prototype._parseMappings =
+ function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+ throw new Error("Subclasses must implement _parseMappings");
+ };
+
+ SourceMapConsumer.GENERATED_ORDER = 1;
+ SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+ /**
+ * Iterate over each mapping between an original source/line/column and a
+ * generated line/column in this source map.
+ *
+ * @param Function aCallback
+ * The function that is called with each mapping.
+ * @param Object aContext
+ * Optional. If specified, this object will be the value of `this` every
+ * time that `aCallback` is called.
+ * @param aOrder
+ * Either `SourceMapConsumer.GENERATED_ORDER` or
+ * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+ * iterate over the mappings sorted by the generated file's line/column
+ * order or the original's source/line/column order, respectively. Defaults to
+ * `SourceMapConsumer.GENERATED_ORDER`.
+ */
+ SourceMapConsumer.prototype.eachMapping =
+ function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+ var context = aContext || null;
+ var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+ var mappings;
+ switch (order) {
+ case SourceMapConsumer.GENERATED_ORDER:
+ mappings = this._generatedMappings;
+ break;
+ case SourceMapConsumer.ORIGINAL_ORDER:
+ mappings = this._originalMappings;
+ break;
+ default:
+ throw new Error("Unknown order of iteration.");
+ }
+
+ var sourceRoot = this.sourceRoot;
+ mappings.map(function (mapping) {
+ var source = mapping.source;
+ if (source != null && sourceRoot != null) {
+ source = util.join(sourceRoot, source);
+ }
+ return {
+ source: source,
+ generatedLine: mapping.generatedLine,
+ generatedColumn: mapping.generatedColumn,
+ originalLine: mapping.originalLine,
+ originalColumn: mapping.originalColumn,
+ name: mapping.name
+ };
+ }).forEach(aCallback, context);
+ };
+
+ /**
+ * Returns all generated line and column information for the original source
+ * and line provided. The only argument is an object with the following
+ * properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source.
+ *
+ * and an array of objects is returned, each with the following properties:
+ *
+ * - line: The line number in the generated source, or null.
+ * - column: The column number in the generated source, or null.
+ */
+ SourceMapConsumer.prototype.allGeneratedPositionsFor =
+ function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
+ // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
+ // returns the index of the closest mapping less than the needle. By
+ // setting needle.originalColumn to Infinity, we thus find the last
+ // mapping for the given line, provided such a mapping exists.
+ var needle = {
+ source: util.getArg(aArgs, 'source'),
+ originalLine: util.getArg(aArgs, 'line'),
+ originalColumn: Infinity
+ };
+
+ if (this.sourceRoot != null) {
+ needle.source = util.relative(this.sourceRoot, needle.source);
+ }
+
+ var mappings = [];
+
+ var index = this._findMapping(needle,
+ this._originalMappings,
+ "originalLine",
+ "originalColumn",
+ util.compareByOriginalPositions);
+ if (index >= 0) {
+ var mapping = this._originalMappings[index];
+
+ while (mapping && mapping.originalLine === needle.originalLine) {
+ mappings.push({
+ line: util.getArg(mapping, 'generatedLine', null),
+ column: util.getArg(mapping, 'generatedColumn', null),
+ lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+ });
+
+ mapping = this._originalMappings[--index];
+ }
+ }
+
+ return mappings.reverse();
+ };
+
+ exports.SourceMapConsumer = SourceMapConsumer;
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var base64VLQ = require('./base64-vlq');
+ var util = require('./util');
+ var ArraySet = require('./array-set').ArraySet;
+ var MappingList = require('./mapping-list').MappingList;
+
+ /**
+ * An instance of the SourceMapGenerator represents a source map which is
+ * being built incrementally. You may pass an object with the following
+ * properties:
+ *
+ * - file: The filename of the generated source.
+ * - sourceRoot: A root for all relative URLs in this source map.
+ */
+ function SourceMapGenerator(aArgs) {
+ if (!aArgs) {
+ aArgs = {};
+ }
+ this._file = util.getArg(aArgs, 'file', null);
+ this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+ this._skipValidation = util.getArg(aArgs, 'skipValidation', false);
+ this._sources = new ArraySet();
+ this._names = new ArraySet();
+ this._mappings = new MappingList();
+ this._sourcesContents = null;
+ }
+
+ SourceMapGenerator.prototype._version = 3;
+
+ /**
+ * Creates a new SourceMapGenerator based on a SourceMapConsumer
+ *
+ * @param aSourceMapConsumer The SourceMap.
+ */
+ SourceMapGenerator.fromSourceMap =
+ function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
+ var sourceRoot = aSourceMapConsumer.sourceRoot;
+ var generator = new SourceMapGenerator({
+ file: aSourceMapConsumer.file,
+ sourceRoot: sourceRoot
+ });
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ var newMapping = {
+ generated: {
+ line: mapping.generatedLine,
+ column: mapping.generatedColumn
+ }
+ };
+
+ if (mapping.source != null) {
+ newMapping.source = mapping.source;
+ if (sourceRoot != null) {
+ newMapping.source = util.relative(sourceRoot, newMapping.source);
+ }
+
+ newMapping.original = {
+ line: mapping.originalLine,
+ column: mapping.originalColumn
+ };
+
+ if (mapping.name != null) {
+ newMapping.name = mapping.name;
+ }
+ }
+
+ generator.addMapping(newMapping);
+ });
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content != null) {
+ generator.setSourceContent(sourceFile, content);
+ }
+ });
+ return generator;
+ };
+
+ /**
+ * Add a single mapping from original source line and column to the generated
+ * source's line and column for this source map being created. The mapping
+ * object should have the following properties:
+ *
+ * - generated: An object with the generated line and column positions.
+ * - original: An object with the original line and column positions.
+ * - source: The original source file (relative to the sourceRoot).
+ * - name: An optional original token name for this mapping.
+ */
+ SourceMapGenerator.prototype.addMapping =
+ function SourceMapGenerator_addMapping(aArgs) {
+ var generated = util.getArg(aArgs, 'generated');
+ var original = util.getArg(aArgs, 'original', null);
+ var source = util.getArg(aArgs, 'source', null);
+ var name = util.getArg(aArgs, 'name', null);
+
+ if (!this._skipValidation) {
+ this._validateMapping(generated, original, source, name);
+ }
+
+ if (source != null && !this._sources.has(source)) {
+ this._sources.add(source);
+ }
+
+ if (name != null && !this._names.has(name)) {
+ this._names.add(name);
+ }
+
+ this._mappings.add({
+ generatedLine: generated.line,
+ generatedColumn: generated.column,
+ originalLine: original != null && original.line,
+ originalColumn: original != null && original.column,
+ source: source,
+ name: name
+ });
+ };
+
+ /**
+ * Set the source content for a source file.
+ */
+ SourceMapGenerator.prototype.setSourceContent =
+ function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
+ var source = aSourceFile;
+ if (this._sourceRoot != null) {
+ source = util.relative(this._sourceRoot, source);
+ }
+
+ if (aSourceContent != null) {
+ // Add the source content to the _sourcesContents map.
+ // Create a new _sourcesContents map if the property is null.
+ if (!this._sourcesContents) {
+ this._sourcesContents = {};
+ }
+ this._sourcesContents[util.toSetString(source)] = aSourceContent;
+ } else if (this._sourcesContents) {
+ // Remove the source file from the _sourcesContents map.
+ // If the _sourcesContents map is empty, set the property to null.
+ delete this._sourcesContents[util.toSetString(source)];
+ if (Object.keys(this._sourcesContents).length === 0) {
+ this._sourcesContents = null;
+ }
+ }
+ };
+
+ /**
+ * Applies the mappings of a sub-source-map for a specific source file to the
+ * source map being generated. Each mapping to the supplied source file is
+ * rewritten using the supplied source map. Note: The resolution for the
+ * resulting mappings is the minimium of this map and the supplied map.
+ *
+ * @param aSourceMapConsumer The source map to be applied.
+ * @param aSourceFile Optional. The filename of the source file.
+ * If omitted, SourceMapConsumer's file property will be used.
+ * @param aSourceMapPath Optional. The dirname of the path to the source map
+ * to be applied. If relative, it is relative to the SourceMapConsumer.
+ * This parameter is needed when the two source maps aren't in the same
+ * directory, and the source map to be applied contains relative source
+ * paths. If so, those relative source paths need to be rewritten
+ * relative to the SourceMapGenerator.
+ */
+ SourceMapGenerator.prototype.applySourceMap =
+ function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
+ var sourceFile = aSourceFile;
+ // If aSourceFile is omitted, we will use the file property of the SourceMap
+ if (aSourceFile == null) {
+ if (aSourceMapConsumer.file == null) {
+ throw new Error(
+ 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
+ 'or the source map\'s "file" property. Both were omitted.'
+ );
+ }
+ sourceFile = aSourceMapConsumer.file;
+ }
+ var sourceRoot = this._sourceRoot;
+ // Make "sourceFile" relative if an absolute Url is passed.
+ if (sourceRoot != null) {
+ sourceFile = util.relative(sourceRoot, sourceFile);
+ }
+ // Applying the SourceMap can add and remove items from the sources and
+ // the names array.
+ var newSources = new ArraySet();
+ var newNames = new ArraySet();
+
+ // Find mappings for the "sourceFile"
+ this._mappings.unsortedForEach(function (mapping) {
+ if (mapping.source === sourceFile && mapping.originalLine != null) {
+ // Check if it can be mapped by the source map, then update the mapping.
+ var original = aSourceMapConsumer.originalPositionFor({
+ line: mapping.originalLine,
+ column: mapping.originalColumn
+ });
+ if (original.source != null) {
+ // Copy mapping
+ mapping.source = original.source;
+ if (aSourceMapPath != null) {
+ mapping.source = util.join(aSourceMapPath, mapping.source)
+ }
+ if (sourceRoot != null) {
+ mapping.source = util.relative(sourceRoot, mapping.source);
+ }
+ mapping.originalLine = original.line;
+ mapping.originalColumn = original.column;
+ if (original.name != null) {
+ mapping.name = original.name;
+ }
+ }
+ }
+
+ var source = mapping.source;
+ if (source != null && !newSources.has(source)) {
+ newSources.add(source);
+ }
+
+ var name = mapping.name;
+ if (name != null && !newNames.has(name)) {
+ newNames.add(name);
+ }
+
+ }, this);
+ this._sources = newSources;
+ this._names = newNames;
+
+ // Copy sourcesContents of applied map.
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content != null) {
+ if (aSourceMapPath != null) {
+ sourceFile = util.join(aSourceMapPath, sourceFile);
+ }
+ if (sourceRoot != null) {
+ sourceFile = util.relative(sourceRoot, sourceFile);
+ }
+ this.setSourceContent(sourceFile, content);
+ }
+ }, this);
+ };
+
+ /**
+ * A mapping can have one of the three levels of data:
+ *
+ * 1. Just the generated position.
+ * 2. The Generated position, original position, and original source.
+ * 3. Generated and original position, original source, as well as a name
+ * token.
+ *
+ * To maintain consistency, we validate that any new mapping being added falls
+ * in to one of these categories.
+ */
+ SourceMapGenerator.prototype._validateMapping =
+ function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+ aName) {
+ if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+ && aGenerated.line > 0 && aGenerated.column >= 0
+ && !aOriginal && !aSource && !aName) {
+ // Case 1.
+ return;
+ }
+ else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+ && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+ && aGenerated.line > 0 && aGenerated.column >= 0
+ && aOriginal.line > 0 && aOriginal.column >= 0
+ && aSource) {
+ // Cases 2 and 3.
+ return;
+ }
+ else {
+ throw new Error('Invalid mapping: ' + JSON.stringify({
+ generated: aGenerated,
+ source: aSource,
+ original: aOriginal,
+ name: aName
+ }));
+ }
+ };
+
+ /**
+ * Serialize the accumulated mappings in to the stream of base 64 VLQs
+ * specified by the source map format.
+ */
+ SourceMapGenerator.prototype._serializeMappings =
+ function SourceMapGenerator_serializeMappings() {
+ var previousGeneratedColumn = 0;
+ var previousGeneratedLine = 1;
+ var previousOriginalColumn = 0;
+ var previousOriginalLine = 0;
+ var previousName = 0;
+ var previousSource = 0;
+ var result = '';
+ var mapping;
+
+ var mappings = this._mappings.toArray();
+
+ for (var i = 0, len = mappings.length; i < len; i++) {
+ mapping = mappings[i];
+
+ if (mapping.generatedLine !== previousGeneratedLine) {
+ previousGeneratedColumn = 0;
+ while (mapping.generatedLine !== previousGeneratedLine) {
+ result += ';';
+ previousGeneratedLine++;
+ }
+ }
+ else {
+ if (i > 0) {
+ if (!util.compareByGeneratedPositions(mapping, mappings[i - 1])) {
+ continue;
+ }
+ result += ',';
+ }
+ }
+
+ result += base64VLQ.encode(mapping.generatedColumn
+ - previousGeneratedColumn);
+ previousGeneratedColumn = mapping.generatedColumn;
+
+ if (mapping.source != null) {
+ result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+ - previousSource);
+ previousSource = this._sources.indexOf(mapping.source);
+
+ // lines are stored 0-based in SourceMap spec version 3
+ result += base64VLQ.encode(mapping.originalLine - 1
+ - previousOriginalLine);
+ previousOriginalLine = mapping.originalLine - 1;
+
+ result += base64VLQ.encode(mapping.originalColumn
+ - previousOriginalColumn);
+ previousOriginalColumn = mapping.originalColumn;
+
+ if (mapping.name != null) {
+ result += base64VLQ.encode(this._names.indexOf(mapping.name)
+ - previousName);
+ previousName = this._names.indexOf(mapping.name);
+ }
+ }
+ }
+
+ return result;
+ };
+
+ SourceMapGenerator.prototype._generateSourcesContent =
+ function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
+ return aSources.map(function (source) {
+ if (!this._sourcesContents) {
+ return null;
+ }
+ if (aSourceRoot != null) {
+ source = util.relative(aSourceRoot, source);
+ }
+ var key = util.toSetString(source);
+ return Object.prototype.hasOwnProperty.call(this._sourcesContents,
+ key)
+ ? this._sourcesContents[key]
+ : null;
+ }, this);
+ };
+
+ /**
+ * Externalize the source map.
+ */
+ SourceMapGenerator.prototype.toJSON =
+ function SourceMapGenerator_toJSON() {
+ var map = {
+ version: this._version,
+ sources: this._sources.toArray(),
+ names: this._names.toArray(),
+ mappings: this._serializeMappings()
+ };
+ if (this._file != null) {
+ map.file = this._file;
+ }
+ if (this._sourceRoot != null) {
+ map.sourceRoot = this._sourceRoot;
+ }
+ if (this._sourcesContents) {
+ map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
+ }
+
+ return map;
+ };
+
+ /**
+ * Render the source map being generated to a string.
+ */
+ SourceMapGenerator.prototype.toString =
+ function SourceMapGenerator_toString() {
+ return JSON.stringify(this);
+ };
+
+ exports.SourceMapGenerator = SourceMapGenerator;
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
+ var util = require('./util');
+
+ // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
+ // operating systems these days (capturing the result).
+ var REGEX_NEWLINE = /(\r?\n)/;
+
+ // Newline character code for charCodeAt() comparisons
+ var NEWLINE_CODE = 10;
+
+ // Private symbol for identifying `SourceNode`s when multiple versions of
+ // the source-map library are loaded. This MUST NOT CHANGE across
+ // versions!
+ var isSourceNode = "$$$isSourceNode$$$";
+
+ /**
+ * SourceNodes provide a way to abstract over interpolating/concatenating
+ * snippets of generated JavaScript source code while maintaining the line and
+ * column information associated with the original source code.
+ *
+ * @param aLine The original line number.
+ * @param aColumn The original column number.
+ * @param aSource The original source's filename.
+ * @param aChunks Optional. An array of strings which are snippets of
+ * generated JS, or other SourceNodes.
+ * @param aName The original identifier.
+ */
+ function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
+ this.children = [];
+ this.sourceContents = {};
+ this.line = aLine == null ? null : aLine;
+ this.column = aColumn == null ? null : aColumn;
+ this.source = aSource == null ? null : aSource;
+ this.name = aName == null ? null : aName;
+ this[isSourceNode] = true;
+ if (aChunks != null) this.add(aChunks);
+ }
+
+ /**
+ * Creates a SourceNode from generated code and a SourceMapConsumer.
+ *
+ * @param aGeneratedCode The generated code
+ * @param aSourceMapConsumer The SourceMap for the generated code
+ * @param aRelativePath Optional. The path that relative sources in the
+ * SourceMapConsumer should be relative to.
+ */
+ SourceNode.fromStringWithSourceMap =
+ function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
+ // The SourceNode we want to fill with the generated code
+ // and the SourceMap
+ var node = new SourceNode();
+
+ // All even indices of this array are one line of the generated code,
+ // while all odd indices are the newlines between two adjacent lines
+ // (since `REGEX_NEWLINE` captures its match).
+ // Processed fragments are removed from this array, by calling `shiftNextLine`.
+ var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+ var shiftNextLine = function() {
+ var lineContents = remainingLines.shift();
+ // The last line of a file might not have a newline.
+ var newLine = remainingLines.shift() || "";
+ return lineContents + newLine;
+ };
+
+ // We need to remember the position of "remainingLines"
+ var lastGeneratedLine = 1, lastGeneratedColumn = 0;
+
+ // The generate SourceNodes we need a code range.
+ // To extract it current and last mapping is used.
+ // Here we store the last mapping.
+ var lastMapping = null;
+
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ if (lastMapping !== null) {
+ // We add the code from "lastMapping" to "mapping":
+ // First check if there is a new line in between.
+ if (lastGeneratedLine < mapping.generatedLine) {
+ var code = "";
+ // Associate first line with "lastMapping"
+ addMappingWithCode(lastMapping, shiftNextLine());
+ lastGeneratedLine++;
+ lastGeneratedColumn = 0;
+ // The remaining code is added without mapping
+ } else {
+ // There is no new line in between.
+ // Associate the code between "lastGeneratedColumn" and
+ // "mapping.generatedColumn" with "lastMapping"
+ var nextLine = remainingLines[0];
+ var code = nextLine.substr(0, mapping.generatedColumn -
+ lastGeneratedColumn);
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+ lastGeneratedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ addMappingWithCode(lastMapping, code);
+ // No more remaining code, continue
+ lastMapping = mapping;
+ return;
+ }
+ }
+ // We add the generated code until the first mapping
+ // to the SourceNode without any mapping.
+ // Each line is added as separate string.
+ while (lastGeneratedLine < mapping.generatedLine) {
+ node.add(shiftNextLine());
+ lastGeneratedLine++;
+ }
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ var nextLine = remainingLines[0];
+ node.add(nextLine.substr(0, mapping.generatedColumn));
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
+ lastMapping = mapping;
+ }, this);
+ // We have processed all mappings.
+ if (remainingLines.length > 0) {
+ if (lastMapping) {
+ // Associate the remaining code in the current line with "lastMapping"
+ addMappingWithCode(lastMapping, shiftNextLine());
+ }
+ // and add the remaining lines without any mapping
+ node.add(remainingLines.join(""));
+ }
+
+ // Copy sourcesContent into SourceNode
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content != null) {
+ if (aRelativePath != null) {
+ sourceFile = util.join(aRelativePath, sourceFile);
+ }
+ node.setSourceContent(sourceFile, content);
+ }
+ });
+
+ return node;
+
+ function addMappingWithCode(mapping, code) {
+ if (mapping === null || mapping.source === undefined) {
+ node.add(code);
+ } else {
+ var source = aRelativePath
+ ? util.join(aRelativePath, mapping.source)
+ : mapping.source;
+ node.add(new SourceNode(mapping.originalLine,
+ mapping.originalColumn,
+ source,
+ code,
+ mapping.name));
+ }
+ }
+ };
+
+ /**
+ * Add a chunk of generated JS to this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ SourceNode.prototype.add = function SourceNode_add(aChunk) {
+ if (Array.isArray(aChunk)) {
+ aChunk.forEach(function (chunk) {
+ this.add(chunk);
+ }, this);
+ }
+ else if (aChunk[isSourceNode] || typeof aChunk === "string") {
+ if (aChunk) {
+ this.children.push(aChunk);
+ }
+ }
+ else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+ );
+ }
+ return this;
+ };
+
+ /**
+ * Add a chunk of generated JS to the beginning of this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+ if (Array.isArray(aChunk)) {
+ for (var i = aChunk.length-1; i >= 0; i--) {
+ this.prepend(aChunk[i]);
+ }
+ }
+ else if (aChunk[isSourceNode] || typeof aChunk === "string") {
+ this.children.unshift(aChunk);
+ }
+ else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+ );
+ }
+ return this;
+ };
+
+ /**
+ * Walk over the tree of JS snippets in this node and its children. The
+ * walking function is called once for each snippet of JS and is passed that
+ * snippet and the its original associated source's line/column location.
+ *
+ * @param aFn The traversal function.
+ */
+ SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+ var chunk;
+ for (var i = 0, len = this.children.length; i < len; i++) {
+ chunk = this.children[i];
+ if (chunk[isSourceNode]) {
+ chunk.walk(aFn);
+ }
+ else {
+ if (chunk !== '') {
+ aFn(chunk, { source: this.source,
+ line: this.line,
+ column: this.column,
+ name: this.name });
+ }
+ }
+ }
+ };
+
+ /**
+ * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+ * each of `this.children`.
+ *
+ * @param aSep The separator.
+ */
+ SourceNode.prototype.join = function SourceNode_join(aSep) {
+ var newChildren;
+ var i;
+ var len = this.children.length;
+ if (len > 0) {
+ newChildren = [];
+ for (i = 0; i < len-1; i++) {
+ newChildren.push(this.children[i]);
+ newChildren.push(aSep);
+ }
+ newChildren.push(this.children[i]);
+ this.children = newChildren;
+ }
+ return this;
+ };
+
+ /**
+ * Call String.prototype.replace on the very right-most source snippet. Useful
+ * for trimming whitespace from the end of a source node, etc.
+ *
+ * @param aPattern The pattern to replace.
+ * @param aReplacement The thing to replace the pattern with.
+ */
+ SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+ var lastChild = this.children[this.children.length - 1];
+ if (lastChild[isSourceNode]) {
+ lastChild.replaceRight(aPattern, aReplacement);
+ }
+ else if (typeof lastChild === 'string') {
+ this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
+ }
+ else {
+ this.children.push(''.replace(aPattern, aReplacement));
+ }
+ return this;
+ };
+
+ /**
+ * Set the source content for a source file. This will be added to the SourceMapGenerator
+ * in the sourcesContent field.
+ *
+ * @param aSourceFile The filename of the source file
+ * @param aSourceContent The content of the source file
+ */
+ SourceNode.prototype.setSourceContent =
+ function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
+ this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+ };
+
+ /**
+ * Walk over the tree of SourceNodes. The walking function is called for each
+ * source file content and is passed the filename and source content.
+ *
+ * @param aFn The traversal function.
+ */
+ SourceNode.prototype.walkSourceContents =
+ function SourceNode_walkSourceContents(aFn) {
+ for (var i = 0, len = this.children.length; i < len; i++) {
+ if (this.children[i][isSourceNode]) {
+ this.children[i].walkSourceContents(aFn);
+ }
+ }
+
+ var sources = Object.keys(this.sourceContents);
+ for (var i = 0, len = sources.length; i < len; i++) {
+ aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
+ }
+ };
+
+ /**
+ * Return the string representation of this source node. Walks over the tree
+ * and concatenates all the various snippets together to one string.
+ */
+ SourceNode.prototype.toString = function SourceNode_toString() {
+ var str = "";
+ this.walk(function (chunk) {
+ str += chunk;
+ });
+ return str;
+ };
+
+ /**
+ * Returns the string representation of this source node along with a source
+ * map.
+ */
+ SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+ var generated = {
+ code: "",
+ line: 1,
+ column: 0
+ };
+ var map = new SourceMapGenerator(aArgs);
+ var sourceMappingActive = false;
+ var lastOriginalSource = null;
+ var lastOriginalLine = null;
+ var lastOriginalColumn = null;
+ var lastOriginalName = null;
+ this.walk(function (chunk, original) {
+ generated.code += chunk;
+ if (original.source !== null
+ && original.line !== null
+ && original.column !== null) {
+ if(lastOriginalSource !== original.source
+ || lastOriginalLine !== original.line
+ || lastOriginalColumn !== original.column
+ || lastOriginalName !== original.name) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column
+ },
+ name: original.name
+ });
+ }
+ lastOriginalSource = original.source;
+ lastOriginalLine = original.line;
+ lastOriginalColumn = original.column;
+ lastOriginalName = original.name;
+ sourceMappingActive = true;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ generated: {
+ line: generated.line,
+ column: generated.column
+ }
+ });
+ lastOriginalSource = null;
+ sourceMappingActive = false;
+ }
+ for (var idx = 0, length = chunk.length; idx < length; idx++) {
+ if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
+ generated.line++;
+ generated.column = 0;
+ // Mappings end at eol
+ if (idx + 1 === length) {
+ lastOriginalSource = null;
+ sourceMappingActive = false;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column
+ },
+ name: original.name
+ });
+ }
+ } else {
+ generated.column++;
+ }
+ }
+ });
+ this.walkSourceContents(function (sourceFile, sourceContent) {
+ map.setSourceContent(sourceFile, sourceContent);
+ });
+
+ return { code: generated.code, map: map };
+ };
+
+ exports.SourceNode = SourceNode;
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ /**
+ * This is a helper function for getting values from parameter/options
+ * objects.
+ *
+ * @param args The object we are extracting values from
+ * @param name The name of the property we are getting.
+ * @param defaultValue An optional value to return if the property is missing
+ * from the object. If this is not specified and the property is missing, an
+ * error will be thrown.
+ */
+ function getArg(aArgs, aName, aDefaultValue) {
+ if (aName in aArgs) {
+ return aArgs[aName];
+ } else if (arguments.length === 3) {
+ return aDefaultValue;
+ } else {
+ throw new Error('"' + aName + '" is a required argument.');
+ }
+ }
+ exports.getArg = getArg;
+
+ var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+ var dataUrlRegexp = /^data:.+\,.+$/;
+
+ function urlParse(aUrl) {
+ var match = aUrl.match(urlRegexp);
+ if (!match) {
+ return null;
+ }
+ return {
+ scheme: match[1],
+ auth: match[2],
+ host: match[3],
+ port: match[4],
+ path: match[5]
+ };
+ }
+ exports.urlParse = urlParse;
+
+ function urlGenerate(aParsedUrl) {
+ var url = '';
+ if (aParsedUrl.scheme) {
+ url += aParsedUrl.scheme + ':';
+ }
+ url += '//';
+ if (aParsedUrl.auth) {
+ url += aParsedUrl.auth + '@';
+ }
+ if (aParsedUrl.host) {
+ url += aParsedUrl.host;
+ }
+ if (aParsedUrl.port) {
+ url += ":" + aParsedUrl.port
+ }
+ if (aParsedUrl.path) {
+ url += aParsedUrl.path;
+ }
+ return url;
+ }
+ exports.urlGenerate = urlGenerate;
+
+ /**
+ * Normalizes a path, or the path portion of a URL:
+ *
+ * - Replaces consequtive slashes with one slash.
+ * - Removes unnecessary '.' parts.
+ * - Removes unnecessary '<dir>/..' parts.
+ *
+ * Based on code in the Node.js 'path' core module.
+ *
+ * @param aPath The path or url to normalize.
+ */
+ function normalize(aPath) {
+ var path = aPath;
+ var url = urlParse(aPath);
+ if (url) {
+ if (!url.path) {
+ return aPath;
+ }
+ path = url.path;
+ }
+ var isAbsolute = (path.charAt(0) === '/');
+
+ var parts = path.split(/\/+/);
+ for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
+ part = parts[i];
+ if (part === '.') {
+ parts.splice(i, 1);
+ } else if (part === '..') {
+ up++;
+ } else if (up > 0) {
+ if (part === '') {
+ // The first part is blank if the path is absolute. Trying to go
+ // above the root is a no-op. Therefore we can remove all '..' parts
+ // directly after the root.
+ parts.splice(i + 1, up);
+ up = 0;
+ } else {
+ parts.splice(i, 2);
+ up--;
+ }
+ }
+ }
+ path = parts.join('/');
+
+ if (path === '') {
+ path = isAbsolute ? '/' : '.';
+ }
+
+ if (url) {
+ url.path = path;
+ return urlGenerate(url);
+ }
+ return path;
+ }
+ exports.normalize = normalize;
+
+ /**
+ * Joins two paths/URLs.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be joined with the root.
+ *
+ * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
+ * scheme-relative URL: Then the scheme of aRoot, if any, is prepended
+ * first.
+ * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
+ * is updated with the result and aRoot is returned. Otherwise the result
+ * is returned.
+ * - If aPath is absolute, the result is aPath.
+ * - Otherwise the two paths are joined with a slash.
+ * - Joining for example 'http://' and 'www.example.com' is also supported.
+ */
+ function join(aRoot, aPath) {
+ if (aRoot === "") {
+ aRoot = ".";
+ }
+ if (aPath === "") {
+ aPath = ".";
+ }
+ var aPathUrl = urlParse(aPath);
+ var aRootUrl = urlParse(aRoot);
+ if (aRootUrl) {
+ aRoot = aRootUrl.path || '/';
+ }
+
+ // `join(foo, '//www.example.org')`
+ if (aPathUrl && !aPathUrl.scheme) {
+ if (aRootUrl) {
+ aPathUrl.scheme = aRootUrl.scheme;
+ }
+ return urlGenerate(aPathUrl);
+ }
+
+ if (aPathUrl || aPath.match(dataUrlRegexp)) {
+ return aPath;
+ }
+
+ // `join('http://', 'www.example.com')`
+ if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
+ aRootUrl.host = aPath;
+ return urlGenerate(aRootUrl);
+ }
+
+ var joined = aPath.charAt(0) === '/'
+ ? aPath
+ : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
+
+ if (aRootUrl) {
+ aRootUrl.path = joined;
+ return urlGenerate(aRootUrl);
+ }
+ return joined;
+ }
+ exports.join = join;
+
+ /**
+ * Make a path relative to a URL or another path.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be made relative to aRoot.
+ */
+ function relative(aRoot, aPath) {
+ if (aRoot === "") {
+ aRoot = ".";
+ }
+
+ aRoot = aRoot.replace(/\/$/, '');
+
+ // XXX: It is possible to remove this block, and the tests still pass!
+ var url = urlParse(aRoot);
+ if (aPath.charAt(0) == "/" && url && url.path == "/") {
+ return aPath.slice(1);
+ }
+
+ return aPath.indexOf(aRoot + '/') === 0
+ ? aPath.substr(aRoot.length + 1)
+ : aPath;
+ }
+ exports.relative = relative;
+
+ /**
+ * Because behavior goes wacky when you set `__proto__` on objects, we
+ * have to prefix all the strings in our set with an arbitrary character.
+ *
+ * See https://github.com/mozilla/source-map/pull/31 and
+ * https://github.com/mozilla/source-map/issues/30
+ *
+ * @param String aStr
+ */
+ function toSetString(aStr) {
+ return '$' + aStr;
+ }
+ exports.toSetString = toSetString;
+
+ function fromSetString(aStr) {
+ return aStr.substr(1);
+ }
+ exports.fromSetString = fromSetString;
+
+ function strcmp(aStr1, aStr2) {
+ var s1 = aStr1 || "";
+ var s2 = aStr2 || "";
+ return (s1 > s2) - (s1 < s2);
+ }
+
+ /**
+ * Comparator between two mappings where the original positions are compared.
+ *
+ * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+ * mappings with the same original source/line/column, but different generated
+ * line and column the same. Useful when searching for a mapping with a
+ * stubbed out mapping.
+ */
+ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
+ var cmp;
+
+ cmp = strcmp(mappingA.source, mappingB.source);
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalLine - mappingB.originalLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalColumn - mappingB.originalColumn;
+ if (cmp || onlyCompareOriginal) {
+ return cmp;
+ }
+
+ cmp = strcmp(mappingA.name, mappingB.name);
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.generatedLine - mappingB.generatedLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ return mappingA.generatedColumn - mappingB.generatedColumn;
+ };
+ exports.compareByOriginalPositions = compareByOriginalPositions;
+
+ /**
+ * Comparator between two mappings where the generated positions are
+ * compared.
+ *
+ * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+ * mappings with the same generated line and column, but different
+ * source/name/original line and column the same. Useful when searching for a
+ * mapping with a stubbed out mapping.
+ */
+ function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
+ var cmp;
+
+ cmp = mappingA.generatedLine - mappingB.generatedLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+ if (cmp || onlyCompareGenerated) {
+ return cmp;
+ }
+
+ cmp = strcmp(mappingA.source, mappingB.source);
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalLine - mappingB.originalLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalColumn - mappingB.originalColumn;
+ if (cmp) {
+ return cmp;
+ }
+
+ return strcmp(mappingA.name, mappingB.name);
+ };
+ exports.compareByGeneratedPositions = compareByGeneratedPositions;
+
+});
--- /dev/null
+amdefine is released under two licenses: new BSD, and MIT. You may pick the
+license that best suits your development needs. The text of both licenses are
+provided below.
+
+
+The "New" BSD License:
+----------------------
+
+Copyright (c) 2011, The Dojo Foundation
+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 Dojo Foundation 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
+
+
+
+MIT License
+-----------
+
+Copyright (c) 2011, The Dojo Foundation
+
+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.
--- /dev/null
+# amdefine
+
+A module that can be used to implement AMD's define() in Node. This allows you
+to code to the AMD API and have the module work in node programs without
+requiring those other programs to use AMD.
+
+## Usage
+
+**1)** Update your package.json to indicate amdefine as a dependency:
+
+```javascript
+ "dependencies": {
+ "amdefine": ">=0.1.0"
+ }
+```
+
+Then run `npm install` to get amdefine into your project.
+
+**2)** At the top of each module that uses define(), place this code:
+
+```javascript
+if (typeof define !== 'function') { var define = require('amdefine')(module) }
+```
+
+**Only use these snippets** when loading amdefine. If you preserve the basic structure,
+with the braces, it will be stripped out when using the [RequireJS optimizer](#optimizer).
+
+You can add spaces, line breaks and even require amdefine with a local path, but
+keep the rest of the structure to get the stripping behavior.
+
+As you may know, because `if` statements in JavaScript don't have their own scope, the var
+declaration in the above snippet is made whether the `if` expression is truthy or not. If
+RequireJS is loaded then the declaration is superfluous because `define` is already already
+declared in the same scope in RequireJS. Fortunately JavaScript handles multiple `var`
+declarations of the same variable in the same scope gracefully.
+
+If you want to deliver amdefine.js with your code rather than specifying it as a dependency
+with npm, then just download the latest release and refer to it using a relative path:
+
+[Latest Version](https://github.com/jrburke/amdefine/raw/latest/amdefine.js)
+
+### amdefine/intercept
+
+Consider this very experimental.
+
+Instead of pasting the piece of text for the amdefine setup of a `define`
+variable in each module you create or consume, you can use `amdefine/intercept`
+instead. It will automatically insert the above snippet in each .js file loaded
+by Node.
+
+**Warning**: you should only use this if you are creating an application that
+is consuming AMD style defined()'d modules that are distributed via npm and want
+to run that code in Node.
+
+For library code where you are not sure if it will be used by others in Node or
+in the browser, then explicitly depending on amdefine and placing the code
+snippet above is suggested path, instead of using `amdefine/intercept`. The
+intercept module affects all .js files loaded in the Node app, and it is
+inconsiderate to modify global state like that unless you are also controlling
+the top level app.
+
+#### Why distribute AMD-style nodes via npm?
+
+npm has a lot of weaknesses for front-end use (installed layout is not great,
+should have better support for the `baseUrl + moduleID + '.js' style of loading,
+single file JS installs), but some people want a JS package manager and are
+willing to live with those constraints. If that is you, but still want to author
+in AMD style modules to get dynamic require([]), better direct source usage and
+powerful loader plugin support in the browser, then this tool can help.
+
+#### amdefine/intercept usage
+
+Just require it in your top level app module (for example index.js, server.js):
+
+```javascript
+require('amdefine/intercept');
+```
+
+The module does not return a value, so no need to assign the result to a local
+variable.
+
+Then just require() code as you normally would with Node's require(). Any .js
+loaded after the intercept require will have the amdefine check injected in
+the .js source as it is loaded. It does not modify the source on disk, just
+prepends some content to the text of the module as it is loaded by Node.
+
+#### How amdefine/intercept works
+
+It overrides the `Module._extensions['.js']` in Node to automatically prepend
+the amdefine snippet above. So, it will affect any .js file loaded by your
+app.
+
+## define() usage
+
+It is best if you use the anonymous forms of define() in your module:
+
+```javascript
+define(function (require) {
+ var dependency = require('dependency');
+});
+```
+
+or
+
+```javascript
+define(['dependency'], function (dependency) {
+
+});
+```
+
+## RequireJS optimizer integration. <a name="optimizer"></name>
+
+Version 1.0.3 of the [RequireJS optimizer](http://requirejs.org/docs/optimization.html)
+will have support for stripping the `if (typeof define !== 'function')` check
+mentioned above, so you can include this snippet for code that runs in the
+browser, but avoid taking the cost of the if() statement once the code is
+optimized for deployment.
+
+## Node 0.4 Support
+
+If you want to support Node 0.4, then add `require` as the second parameter to amdefine:
+
+```javascript
+//Only if you want Node 0.4. If using 0.5 or later, use the above snippet.
+if (typeof define !== 'function') { var define = require('amdefine')(module, require) }
+```
+
+## Limitations
+
+### Synchronous vs Asynchronous
+
+amdefine creates a define() function that is callable by your code. It will
+execute and trace dependencies and call the factory function *synchronously*,
+to keep the behavior in line with Node's synchronous dependency tracing.
+
+The exception: calling AMD's callback-style require() from inside a factory
+function. The require callback is called on process.nextTick():
+
+```javascript
+define(function (require) {
+ require(['a'], function(a) {
+ //'a' is loaded synchronously, but
+ //this callback is called on process.nextTick().
+ });
+});
+```
+
+### Loader Plugins
+
+Loader plugins are supported as long as they call their load() callbacks
+synchronously. So ones that do network requests will not work. However plugins
+like [text](http://requirejs.org/docs/api.html#text) can load text files locally.
+
+The plugin API's `load.fromText()` is **not supported** in amdefine, so this means
+transpiler plugins like the [CoffeeScript loader plugin](https://github.com/jrburke/require-cs)
+will not work. This may be fixable, but it is a bit complex, and I do not have
+enough node-fu to figure it out yet. See the source for amdefine.js if you want
+to get an idea of the issues involved.
+
+## Tests
+
+To run the tests, cd to **tests** and run:
+
+```
+node all.js
+node all-intercept.js
+```
+
+## License
+
+New BSD and MIT. Check the LICENSE file for all the details.
--- /dev/null
+/** vim: et:ts=4:sw=4:sts=4
+ * @license amdefine 0.1.0 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/amdefine for details
+ */
+
+/*jslint node: true */
+/*global module, process */
+'use strict';
+
+/**
+ * Creates a define for node.
+ * @param {Object} module the "module" object that is defined by Node for the
+ * current module.
+ * @param {Function} [requireFn]. Node's require function for the current module.
+ * It only needs to be passed in Node versions before 0.5, when module.require
+ * did not exist.
+ * @returns {Function} a define function that is usable for the current node
+ * module.
+ */
+function amdefine(module, requireFn) {
+ 'use strict';
+ var defineCache = {},
+ loaderCache = {},
+ alreadyCalled = false,
+ path = require('path'),
+ makeRequire, stringRequire;
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part;
+ for (i = 0; ary[i]; i+= 1) {
+ part = ary[i];
+ if (part === '.') {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === '..') {
+ if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ function normalize(name, baseName) {
+ var baseParts;
+
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === '.') {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ baseParts = baseName.split('/');
+ baseParts = baseParts.slice(0, baseParts.length - 1);
+ baseParts = baseParts.concat(name.split('/'));
+ trimDots(baseParts);
+ name = baseParts.join('/');
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Create the normalize() function passed to a loader plugin's
+ * normalize method.
+ */
+ function makeNormalize(relName) {
+ return function (name) {
+ return normalize(name, relName);
+ };
+ }
+
+ function makeLoad(id) {
+ function load(value) {
+ loaderCache[id] = value;
+ }
+
+ load.fromText = function (id, text) {
+ //This one is difficult because the text can/probably uses
+ //define, and any relative paths and requires should be relative
+ //to that id was it would be found on disk. But this would require
+ //bootstrapping a module/require fairly deeply from node core.
+ //Not sure how best to go about that yet.
+ throw new Error('amdefine does not implement load.fromText');
+ };
+
+ return load;
+ }
+
+ makeRequire = function (systemRequire, exports, module, relId) {
+ function amdRequire(deps, callback) {
+ if (typeof deps === 'string') {
+ //Synchronous, single module require('')
+ return stringRequire(systemRequire, exports, module, deps, relId);
+ } else {
+ //Array of dependencies with a callback.
+
+ //Convert the dependencies to modules.
+ deps = deps.map(function (depName) {
+ return stringRequire(systemRequire, exports, module, depName, relId);
+ });
+
+ //Wait for next tick to call back the require call.
+ process.nextTick(function () {
+ callback.apply(null, deps);
+ });
+ }
+ }
+
+ amdRequire.toUrl = function (filePath) {
+ if (filePath.indexOf('.') === 0) {
+ return normalize(filePath, path.dirname(module.filename));
+ } else {
+ return filePath;
+ }
+ };
+
+ return amdRequire;
+ };
+
+ //Favor explicit value, passed in if the module wants to support Node 0.4.
+ requireFn = requireFn || function req() {
+ return module.require.apply(module, arguments);
+ };
+
+ function runFactory(id, deps, factory) {
+ var r, e, m, result;
+
+ if (id) {
+ e = loaderCache[id] = {};
+ m = {
+ id: id,
+ uri: __filename,
+ exports: e
+ };
+ r = makeRequire(requireFn, e, m, id);
+ } else {
+ //Only support one define call per file
+ if (alreadyCalled) {
+ throw new Error('amdefine with no module ID cannot be called more than once per file.');
+ }
+ alreadyCalled = true;
+
+ //Use the real variables from node
+ //Use module.exports for exports, since
+ //the exports in here is amdefine exports.
+ e = module.exports;
+ m = module;
+ r = makeRequire(requireFn, e, m, module.id);
+ }
+
+ //If there are dependencies, they are strings, so need
+ //to convert them to dependency values.
+ if (deps) {
+ deps = deps.map(function (depName) {
+ return r(depName);
+ });
+ }
+
+ //Call the factory with the right dependencies.
+ if (typeof factory === 'function') {
+ result = factory.apply(m.exports, deps);
+ } else {
+ result = factory;
+ }
+
+ if (result !== undefined) {
+ m.exports = result;
+ if (id) {
+ loaderCache[id] = m.exports;
+ }
+ }
+ }
+
+ stringRequire = function (systemRequire, exports, module, id, relId) {
+ //Split the ID by a ! so that
+ var index = id.indexOf('!'),
+ originalId = id,
+ prefix, plugin;
+
+ if (index === -1) {
+ id = normalize(id, relId);
+
+ //Straight module lookup. If it is one of the special dependencies,
+ //deal with it, otherwise, delegate to node.
+ if (id === 'require') {
+ return makeRequire(systemRequire, exports, module, relId);
+ } else if (id === 'exports') {
+ return exports;
+ } else if (id === 'module') {
+ return module;
+ } else if (loaderCache.hasOwnProperty(id)) {
+ return loaderCache[id];
+ } else if (defineCache[id]) {
+ runFactory.apply(null, defineCache[id]);
+ return loaderCache[id];
+ } else {
+ if(systemRequire) {
+ return systemRequire(originalId);
+ } else {
+ throw new Error('No module with ID: ' + id);
+ }
+ }
+ } else {
+ //There is a plugin in play.
+ prefix = id.substring(0, index);
+ id = id.substring(index + 1, id.length);
+
+ plugin = stringRequire(systemRequire, exports, module, prefix, relId);
+
+ if (plugin.normalize) {
+ id = plugin.normalize(id, makeNormalize(relId));
+ } else {
+ //Normalize the ID normally.
+ id = normalize(id, relId);
+ }
+
+ if (loaderCache[id]) {
+ return loaderCache[id];
+ } else {
+ plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
+
+ return loaderCache[id];
+ }
+ }
+ };
+
+ //Create a define function specific to the module asking for amdefine.
+ function define(id, deps, factory) {
+ if (Array.isArray(id)) {
+ factory = deps;
+ deps = id;
+ id = undefined;
+ } else if (typeof id !== 'string') {
+ factory = id;
+ id = deps = undefined;
+ }
+
+ if (deps && !Array.isArray(deps)) {
+ factory = deps;
+ deps = undefined;
+ }
+
+ if (!deps) {
+ deps = ['require', 'exports', 'module'];
+ }
+
+ //Set up properties for this module. If an ID, then use
+ //internal cache. If no ID, then use the external variables
+ //for this node module.
+ if (id) {
+ //Put the module in deep freeze until there is a
+ //require call for it.
+ defineCache[id] = [id, deps, factory];
+ } else {
+ runFactory(id, deps, factory);
+ }
+ }
+
+ //define.require, which has access to all the values in the
+ //cache. Useful for AMD modules that all have IDs in the file,
+ //but need to finally export a value to node based on one of those
+ //IDs.
+ define.require = function (id) {
+ if (loaderCache[id]) {
+ return loaderCache[id];
+ }
+
+ if (defineCache[id]) {
+ runFactory.apply(null, defineCache[id]);
+ return loaderCache[id];
+ }
+ };
+
+ define.amd = {};
+
+ return define;
+}
+
+module.exports = amdefine;
--- /dev/null
+/*jshint node: true */
+var inserted,
+ Module = require('module'),
+ fs = require('fs'),
+ existingExtFn = Module._extensions['.js'],
+ amdefineRegExp = /amdefine\.js/;
+
+inserted = "if (typeof define !== 'function') {var define = require('amdefine')(module)}";
+
+//From the node/lib/module.js source:
+function stripBOM(content) {
+ // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
+ // because the buffer-to-string conversion in `fs.readFileSync()`
+ // translates it to FEFF, the UTF-16 BOM.
+ if (content.charCodeAt(0) === 0xFEFF) {
+ content = content.slice(1);
+ }
+ return content;
+}
+
+//Also adapted from the node/lib/module.js source:
+function intercept(module, filename) {
+ var content = stripBOM(fs.readFileSync(filename, 'utf8'));
+
+ if (!amdefineRegExp.test(module.id)) {
+ content = inserted + content;
+ }
+
+ module._compile(content, filename);
+}
+
+intercept._id = 'amdefine/intercept';
+
+if (!existingExtFn._id || existingExtFn._id !== intercept._id) {
+ Module._extensions['.js'] = intercept;
+}
--- /dev/null
+{
+ "name": "amdefine",
+ "description": "Provide AMD's define() API for declaring modules in the AMD format",
+ "version": "0.1.0",
+ "homepage": "http://github.com/jrburke/amdefine",
+ "author": {
+ "name": "James Burke",
+ "email": "jrburke@gmail.com",
+ "url": "http://github.com/jrburke"
+ },
+ "licenses": [
+ {
+ "type": "BSD",
+ "url": "https://github.com/jrburke/amdefine/blob/master/LICENSE"
+ },
+ {
+ "type": "MIT",
+ "url": "https://github.com/jrburke/amdefine/blob/master/LICENSE"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jrburke/amdefine.git"
+ },
+ "main": "./amdefine.js",
+ "engines": {
+ "node": ">=0.4.2"
+ },
+ "readme": "# amdefine\n\nA module that can be used to implement AMD's define() in Node. This allows you\nto code to the AMD API and have the module work in node programs without\nrequiring those other programs to use AMD.\n\n## Usage\n\n**1)** Update your package.json to indicate amdefine as a dependency:\n\n```javascript\n \"dependencies\": {\n \"amdefine\": \">=0.1.0\"\n }\n```\n\nThen run `npm install` to get amdefine into your project.\n\n**2)** At the top of each module that uses define(), place this code:\n\n```javascript\nif (typeof define !== 'function') { var define = require('amdefine')(module) }\n```\n\n**Only use these snippets** when loading amdefine. If you preserve the basic structure,\nwith the braces, it will be stripped out when using the [RequireJS optimizer](#optimizer).\n\nYou can add spaces, line breaks and even require amdefine with a local path, but\nkeep the rest of the structure to get the stripping behavior.\n\nAs you may know, because `if` statements in JavaScript don't have their own scope, the var\ndeclaration in the above snippet is made whether the `if` expression is truthy or not. If\nRequireJS is loaded then the declaration is superfluous because `define` is already already\ndeclared in the same scope in RequireJS. Fortunately JavaScript handles multiple `var`\ndeclarations of the same variable in the same scope gracefully.\n\nIf you want to deliver amdefine.js with your code rather than specifying it as a dependency\nwith npm, then just download the latest release and refer to it using a relative path:\n\n[Latest Version](https://github.com/jrburke/amdefine/raw/latest/amdefine.js)\n\n### amdefine/intercept\n\nConsider this very experimental.\n\nInstead of pasting the piece of text for the amdefine setup of a `define`\nvariable in each module you create or consume, you can use `amdefine/intercept`\ninstead. It will automatically insert the above snippet in each .js file loaded\nby Node.\n\n**Warning**: you should only use this if you are creating an application that\nis consuming AMD style defined()'d modules that are distributed via npm and want\nto run that code in Node.\n\nFor library code where you are not sure if it will be used by others in Node or\nin the browser, then explicitly depending on amdefine and placing the code\nsnippet above is suggested path, instead of using `amdefine/intercept`. The\nintercept module affects all .js files loaded in the Node app, and it is\ninconsiderate to modify global state like that unless you are also controlling\nthe top level app.\n\n#### Why distribute AMD-style nodes via npm?\n\nnpm has a lot of weaknesses for front-end use (installed layout is not great,\nshould have better support for the `baseUrl + moduleID + '.js' style of loading,\nsingle file JS installs), but some people want a JS package manager and are\nwilling to live with those constraints. If that is you, but still want to author\nin AMD style modules to get dynamic require([]), better direct source usage and\npowerful loader plugin support in the browser, then this tool can help.\n\n#### amdefine/intercept usage\n\nJust require it in your top level app module (for example index.js, server.js):\n\n```javascript\nrequire('amdefine/intercept');\n```\n\nThe module does not return a value, so no need to assign the result to a local\nvariable.\n\nThen just require() code as you normally would with Node's require(). Any .js\nloaded after the intercept require will have the amdefine check injected in\nthe .js source as it is loaded. It does not modify the source on disk, just\nprepends some content to the text of the module as it is loaded by Node.\n\n#### How amdefine/intercept works\n\nIt overrides the `Module._extensions['.js']` in Node to automatically prepend\nthe amdefine snippet above. So, it will affect any .js file loaded by your\napp.\n\n## define() usage\n\nIt is best if you use the anonymous forms of define() in your module:\n\n```javascript\ndefine(function (require) {\n var dependency = require('dependency');\n});\n```\n\nor\n\n```javascript\ndefine(['dependency'], function (dependency) {\n\n});\n```\n\n## RequireJS optimizer integration. <a name=\"optimizer\"></name>\n\nVersion 1.0.3 of the [RequireJS optimizer](http://requirejs.org/docs/optimization.html)\nwill have support for stripping the `if (typeof define !== 'function')` check\nmentioned above, so you can include this snippet for code that runs in the\nbrowser, but avoid taking the cost of the if() statement once the code is\noptimized for deployment.\n\n## Node 0.4 Support\n\nIf you want to support Node 0.4, then add `require` as the second parameter to amdefine:\n\n```javascript\n//Only if you want Node 0.4. If using 0.5 or later, use the above snippet.\nif (typeof define !== 'function') { var define = require('amdefine')(module, require) }\n```\n\n## Limitations\n\n### Synchronous vs Asynchronous\n\namdefine creates a define() function that is callable by your code. It will\nexecute and trace dependencies and call the factory function *synchronously*,\nto keep the behavior in line with Node's synchronous dependency tracing.\n\nThe exception: calling AMD's callback-style require() from inside a factory\nfunction. The require callback is called on process.nextTick():\n\n```javascript\ndefine(function (require) {\n require(['a'], function(a) {\n //'a' is loaded synchronously, but\n //this callback is called on process.nextTick().\n });\n});\n```\n\n### Loader Plugins\n\nLoader plugins are supported as long as they call their load() callbacks\nsynchronously. So ones that do network requests will not work. However plugins\nlike [text](http://requirejs.org/docs/api.html#text) can load text files locally.\n\nThe plugin API's `load.fromText()` is **not supported** in amdefine, so this means\ntranspiler plugins like the [CoffeeScript loader plugin](https://github.com/jrburke/require-cs)\nwill not work. This may be fixable, but it is a bit complex, and I do not have\nenough node-fu to figure it out yet. See the source for amdefine.js if you want\nto get an idea of the issues involved.\n\n## Tests\n\nTo run the tests, cd to **tests** and run:\n\n```\nnode all.js\nnode all-intercept.js\n```\n\n## License\n\nNew BSD and MIT. Check the LICENSE file for all the details.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/jrburke/amdefine/issues"
+ },
+ "_id": "amdefine@0.1.0",
+ "dist": {
+ "shasum": "3ca9735cf1dde0edf7a4bf6641709c8024f9b227",
+ "tarball": "http://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz"
+ },
+ "_from": "amdefine@>=0.0.4",
+ "_npmVersion": "1.3.8",
+ "_npmUser": {
+ "name": "jrburke",
+ "email": "jrburke@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "jrburke",
+ "email": "jrburke@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "3ca9735cf1dde0edf7a4bf6641709c8024f9b227",
+ "_resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz",
+ "scripts": {}
+}
--- /dev/null
+{
+ "name": "source-map",
+ "description": "Generates and consumes source maps",
+ "version": "0.2.0",
+ "homepage": "https://github.com/mozilla/source-map",
+ "author": {
+ "name": "Nick Fitzgerald",
+ "email": "nfitzgerald@mozilla.com"
+ },
+ "contributors": [
+ {
+ "name": "Tobias Koppers",
+ "email": "tobias.koppers@googlemail.com"
+ },
+ {
+ "name": "Duncan Beevers",
+ "email": "duncan@dweebd.com"
+ },
+ {
+ "name": "Stephen Crane",
+ "email": "scrane@mozilla.com"
+ },
+ {
+ "name": "Ryan Seddon",
+ "email": "seddon.ryan@gmail.com"
+ },
+ {
+ "name": "Miles Elam",
+ "email": "miles.elam@deem.com"
+ },
+ {
+ "name": "Mihai Bazon",
+ "email": "mihai.bazon@gmail.com"
+ },
+ {
+ "name": "Michael Ficarra",
+ "email": "github.public.email@michael.ficarra.me"
+ },
+ {
+ "name": "Todd Wolfson",
+ "email": "todd@twolfson.com"
+ },
+ {
+ "name": "Alexander Solovyov",
+ "email": "alexander@solovyov.net"
+ },
+ {
+ "name": "Felix Gnass",
+ "email": "fgnass@gmail.com"
+ },
+ {
+ "name": "Conrad Irwin",
+ "email": "conrad.irwin@gmail.com"
+ },
+ {
+ "name": "usrbincc",
+ "email": "usrbincc@yahoo.com"
+ },
+ {
+ "name": "David Glasser",
+ "email": "glasser@davidglasser.net"
+ },
+ {
+ "name": "Chase Douglas",
+ "email": "chase@newrelic.com"
+ },
+ {
+ "name": "Evan Wallace",
+ "email": "evan.exe@gmail.com"
+ },
+ {
+ "name": "Heather Arthur",
+ "email": "fayearthur@gmail.com"
+ },
+ {
+ "name": "Hugh Kennedy",
+ "email": "hughskennedy@gmail.com"
+ },
+ {
+ "name": "David Glasser",
+ "email": "glasser@davidglasser.net"
+ },
+ {
+ "name": "Simon Lydell",
+ "email": "simon.lydell@gmail.com"
+ },
+ {
+ "name": "Jmeas Smith",
+ "email": "jellyes2@gmail.com"
+ },
+ {
+ "name": "Michael Z Goddard",
+ "email": "mzgoddard@gmail.com"
+ },
+ {
+ "name": "azu",
+ "email": "azu@users.noreply.github.com"
+ },
+ {
+ "name": "John Gozde",
+ "email": "john@gozde.ca"
+ },
+ {
+ "name": "Adam Kirkton",
+ "email": "akirkton@truefitinnovation.com"
+ },
+ {
+ "name": "Chris Montgomery",
+ "email": "christopher.montgomery@dowjones.com"
+ },
+ {
+ "name": "J. Ryan Stinnett",
+ "email": "jryans@gmail.com"
+ },
+ {
+ "name": "Jack Herrington",
+ "email": "jherrington@walmartlabs.com"
+ },
+ {
+ "name": "Chris Truter",
+ "email": "jeffpalentine@gmail.com"
+ },
+ {
+ "name": "Daniel Espeset",
+ "email": "daniel@danielespeset.com"
+ },
+ {
+ "name": "Jamie Wong",
+ "email": "jamie.lf.wong@gmail.com"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/mozilla/source-map.git"
+ },
+ "directories": {
+ "lib": "./lib"
+ },
+ "main": "./lib/source-map.js",
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "licenses": [
+ {
+ "type": "BSD",
+ "url": "http://opensource.org/licenses/BSD-3-Clause"
+ }
+ ],
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "devDependencies": {
+ "dryice": ">=0.4.8"
+ },
+ "scripts": {
+ "test": "node test/run-tests.js",
+ "build": "node Makefile.dryice.js"
+ },
+ "bugs": {
+ "url": "https://github.com/mozilla/source-map/issues"
+ },
+ "_id": "source-map@0.2.0",
+ "_shasum": "dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d",
+ "_from": "source-map@>=0.2.0 <0.3.0",
+ "_npmVersion": "1.4.9",
+ "_npmUser": {
+ "name": "nickfitzgerald",
+ "email": "fitzgen@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "mozilla-devtools",
+ "email": "mozilla-developer-tools@googlegroups.com"
+ },
+ {
+ "name": "mozilla",
+ "email": "dherman@mozilla.com"
+ },
+ {
+ "name": "nickfitzgerald",
+ "email": "fitzgen@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d",
+ "tarball": "http://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz"
+ },
+ "_resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz"
+}
--- /dev/null
+#!/usr/bin/env node
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var util = require('./source-map/util');
+
+function run(tests) {
+ var total = 0;
+ var passed = 0;
+
+ for (var i = 0; i < tests.length; i++) {
+ for (var k in tests[i].testCase) {
+ if (/^test/.test(k)) {
+ total++;
+ try {
+ tests[i].testCase[k](assert, util);
+ passed++;
+ }
+ catch (e) {
+ console.log('FAILED ' + tests[i].name + ': ' + k + '!');
+ console.log(e.stack);
+ }
+ }
+ }
+ }
+
+ console.log('');
+ console.log(passed + ' / ' + total + ' tests passed.');
+ console.log('');
+
+ return total - passed;
+}
+
+function isTestFile(f) {
+ var testToRun = process.argv[2];
+ return testToRun
+ ? path.basename(testToRun) === f
+ : /^test\-.*?\.js/.test(f);
+}
+
+function toModule(f) {
+ return './source-map/' + f.replace(/\.js$/, '');
+}
+
+var requires = fs.readdirSync(path.join(__dirname, 'source-map'))
+ .filter(isTestFile)
+ .map(toModule);
+
+var code = run(requires.map(require).map(function (mod, i) {
+ return {
+ name: requires[i],
+ testCase: mod
+ };
+}));
+
+process.exit(code);
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2012 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var sourceMap;
+ try {
+ sourceMap = require('../../lib/source-map');
+ } catch (e) {
+ sourceMap = {};
+ Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+ }
+
+ exports['test that the api is properly exposed in the top level'] = function (assert, util) {
+ assert.equal(typeof sourceMap.SourceMapGenerator, "function");
+ assert.equal(typeof sourceMap.SourceMapConsumer, "function");
+ assert.equal(typeof sourceMap.SourceNode, "function");
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var ArraySet = require('../../lib/source-map/array-set').ArraySet;
+
+ function makeTestSet() {
+ var set = new ArraySet();
+ for (var i = 0; i < 100; i++) {
+ set.add(String(i));
+ }
+ return set;
+ }
+
+ exports['test .has() membership'] = function (assert, util) {
+ var set = makeTestSet();
+ for (var i = 0; i < 100; i++) {
+ assert.ok(set.has(String(i)));
+ }
+ };
+
+ exports['test .indexOf() elements'] = function (assert, util) {
+ var set = makeTestSet();
+ for (var i = 0; i < 100; i++) {
+ assert.strictEqual(set.indexOf(String(i)), i);
+ }
+ };
+
+ exports['test .at() indexing'] = function (assert, util) {
+ var set = makeTestSet();
+ for (var i = 0; i < 100; i++) {
+ assert.strictEqual(set.at(i), String(i));
+ }
+ };
+
+ exports['test creating from an array'] = function (assert, util) {
+ var set = ArraySet.fromArray(['foo', 'bar', 'baz', 'quux', 'hasOwnProperty']);
+
+ assert.ok(set.has('foo'));
+ assert.ok(set.has('bar'));
+ assert.ok(set.has('baz'));
+ assert.ok(set.has('quux'));
+ assert.ok(set.has('hasOwnProperty'));
+
+ assert.strictEqual(set.indexOf('foo'), 0);
+ assert.strictEqual(set.indexOf('bar'), 1);
+ assert.strictEqual(set.indexOf('baz'), 2);
+ assert.strictEqual(set.indexOf('quux'), 3);
+
+ assert.strictEqual(set.at(0), 'foo');
+ assert.strictEqual(set.at(1), 'bar');
+ assert.strictEqual(set.at(2), 'baz');
+ assert.strictEqual(set.at(3), 'quux');
+ };
+
+ exports['test that you can add __proto__; see github issue #30'] = function (assert, util) {
+ var set = new ArraySet();
+ set.add('__proto__');
+ assert.ok(set.has('__proto__'));
+ assert.strictEqual(set.at(0), '__proto__');
+ assert.strictEqual(set.indexOf('__proto__'), 0);
+ };
+
+ exports['test .fromArray() with duplicates'] = function (assert, util) {
+ var set = ArraySet.fromArray(['foo', 'foo']);
+ assert.ok(set.has('foo'));
+ assert.strictEqual(set.at(0), 'foo');
+ assert.strictEqual(set.indexOf('foo'), 0);
+ assert.strictEqual(set.toArray().length, 1);
+
+ set = ArraySet.fromArray(['foo', 'foo'], true);
+ assert.ok(set.has('foo'));
+ assert.strictEqual(set.at(0), 'foo');
+ assert.strictEqual(set.at(1), 'foo');
+ assert.strictEqual(set.indexOf('foo'), 0);
+ assert.strictEqual(set.toArray().length, 2);
+ };
+
+ exports['test .add() with duplicates'] = function (assert, util) {
+ var set = new ArraySet();
+ set.add('foo');
+
+ set.add('foo');
+ assert.ok(set.has('foo'));
+ assert.strictEqual(set.at(0), 'foo');
+ assert.strictEqual(set.indexOf('foo'), 0);
+ assert.strictEqual(set.toArray().length, 1);
+
+ set.add('foo', true);
+ assert.ok(set.has('foo'));
+ assert.strictEqual(set.at(0), 'foo');
+ assert.strictEqual(set.at(1), 'foo');
+ assert.strictEqual(set.indexOf('foo'), 0);
+ assert.strictEqual(set.toArray().length, 2);
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var base64VLQ = require('../../lib/source-map/base64-vlq');
+
+ exports['test normal encoding and decoding'] = function (assert, util) {
+ var result = {};
+ for (var i = -255; i < 256; i++) {
+ base64VLQ.decode(base64VLQ.encode(i), result);
+ assert.equal(result.value, i);
+ assert.equal(result.rest, "");
+ }
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var base64 = require('../../lib/source-map/base64');
+
+ exports['test out of range encoding'] = function (assert, util) {
+ assert.throws(function () {
+ base64.encode(-1);
+ });
+ assert.throws(function () {
+ base64.encode(64);
+ });
+ };
+
+ exports['test out of range decoding'] = function (assert, util) {
+ assert.throws(function () {
+ base64.decode('=');
+ });
+ };
+
+ exports['test normal encoding and decoding'] = function (assert, util) {
+ for (var i = 0; i < 64; i++) {
+ assert.equal(base64.decode(base64.encode(i)), i);
+ }
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var binarySearch = require('../../lib/source-map/binary-search');
+
+ function numberCompare(a, b) {
+ return a - b;
+ }
+
+ exports['test too high'] = function (assert, util) {
+ var needle = 30;
+ var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+ assert.doesNotThrow(function () {
+ binarySearch.search(needle, haystack, numberCompare);
+ });
+
+ assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare)], 20);
+ };
+
+ exports['test too low'] = function (assert, util) {
+ var needle = 1;
+ var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+ assert.doesNotThrow(function () {
+ binarySearch.search(needle, haystack, numberCompare);
+ });
+
+ assert.equal(binarySearch.search(needle, haystack, numberCompare), -1);
+ };
+
+ exports['test exact search'] = function (assert, util) {
+ var needle = 4;
+ var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+ assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare)], 4);
+ };
+
+ exports['test fuzzy search'] = function (assert, util) {
+ var needle = 19;
+ var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+ assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare)], 18);
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+ var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+
+ exports['test eating our own dog food'] = function (assert, util) {
+ var smg = new SourceMapGenerator({
+ file: 'testing.js',
+ sourceRoot: '/wu/tang'
+ });
+
+ smg.addMapping({
+ source: 'gza.coffee',
+ original: { line: 1, column: 0 },
+ generated: { line: 2, column: 2 }
+ });
+
+ smg.addMapping({
+ source: 'gza.coffee',
+ original: { line: 2, column: 0 },
+ generated: { line: 3, column: 2 }
+ });
+
+ smg.addMapping({
+ source: 'gza.coffee',
+ original: { line: 3, column: 0 },
+ generated: { line: 4, column: 2 }
+ });
+
+ smg.addMapping({
+ source: 'gza.coffee',
+ original: { line: 4, column: 0 },
+ generated: { line: 5, column: 2 }
+ });
+
+ smg.addMapping({
+ source: 'gza.coffee',
+ original: { line: 5, column: 10 },
+ generated: { line: 6, column: 12 }
+ });
+
+ var smc = new SourceMapConsumer(smg.toString());
+
+ // Exact
+ util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
+ util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
+ util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
+ util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
+ util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 10, null, smc, assert);
+
+ // Fuzzy
+
+ // Generated to original
+ util.assertMapping(2, 0, null, null, null, null, smc, assert, true);
+ util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, smc, assert, true);
+ util.assertMapping(3, 0, null, null, null, null, smc, assert, true);
+ util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, smc, assert, true);
+ util.assertMapping(4, 0, null, null, null, null, smc, assert, true);
+ util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, smc, assert, true);
+ util.assertMapping(5, 0, null, null, null, null, smc, assert, true);
+ util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, smc, assert, true);
+ util.assertMapping(6, 0, null, null, null, null, smc, assert, true);
+ util.assertMapping(6, 9, null, null, null, null, smc, assert, true);
+ util.assertMapping(6, 13, '/wu/tang/gza.coffee', 5, 10, null, smc, assert, true);
+
+ // Original to generated
+ util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 1, null, smc, assert, null, true);
+ util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 3, null, smc, assert, null, true);
+ util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 6, null, smc, assert, null, true);
+ util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 9, null, smc, assert, null, true);
+ util.assertMapping(5, 2, '/wu/tang/gza.coffee', 5, 9, null, smc, assert, null, true);
+ util.assertMapping(6, 12, '/wu/tang/gza.coffee', 6, 19, null, smc, assert, null, true);
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+ var IndexedSourceMapConsumer = require('../../lib/source-map/indexed-source-map-consumer').IndexedSourceMapConsumer;
+ var BasicSourceMapConsumer = require('../../lib/source-map/basic-source-map-consumer').BasicSourceMapConsumer;
+ var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+
+ exports['test that we can instantiate with a string or an object'] = function (assert, util) {
+ assert.doesNotThrow(function () {
+ var map = new SourceMapConsumer(util.testMap);
+ });
+ assert.doesNotThrow(function () {
+ var map = new SourceMapConsumer(JSON.stringify(util.testMap));
+ });
+ };
+
+ exports['test that the object returned from new SourceMapConsumer inherits from SourceMapConsumer'] = function (assert, util) {
+ assert.ok(new SourceMapConsumer(util.testMap) instanceof SourceMapConsumer);
+ }
+
+ exports['test that a BasicSourceMapConsumer is returned for sourcemaps without sections'] = function(assert, util) {
+ assert.ok(new SourceMapConsumer(util.testMap) instanceof BasicSourceMapConsumer);
+ };
+
+ exports['test that an IndexedSourceMapConsumer is returned for sourcemaps with sections'] = function(assert, util) {
+ assert.ok(new SourceMapConsumer(util.indexedTestMap) instanceof IndexedSourceMapConsumer);
+ };
+
+ exports['test that the `sources` field has the original sources'] = function (assert, util) {
+ var map;
+ var sources;
+
+ map = new SourceMapConsumer(util.testMap);
+ sources = map.sources;
+ assert.equal(sources[0], '/the/root/one.js');
+ assert.equal(sources[1], '/the/root/two.js');
+ assert.equal(sources.length, 2);
+
+ map = new SourceMapConsumer(util.indexedTestMap);
+ sources = map.sources;
+ assert.equal(sources[0], '/the/root/one.js');
+ assert.equal(sources[1], '/the/root/two.js');
+ assert.equal(sources.length, 2);
+
+ map = new SourceMapConsumer(util.indexedTestMapDifferentSourceRoots);
+ sources = map.sources;
+ assert.equal(sources[0], '/the/root/one.js');
+ assert.equal(sources[1], '/different/root/two.js');
+ assert.equal(sources.length, 2);
+
+ map = new SourceMapConsumer(util.testMapNoSourceRoot);
+ sources = map.sources;
+ assert.equal(sources[0], 'one.js');
+ assert.equal(sources[1], 'two.js');
+ assert.equal(sources.length, 2);
+
+ map = new SourceMapConsumer(util.testMapEmptySourceRoot);
+ sources = map.sources;
+ assert.equal(sources[0], 'one.js');
+ assert.equal(sources[1], 'two.js');
+ assert.equal(sources.length, 2);
+ };
+
+ exports['test that the source root is reflected in a mapping\'s source field'] = function (assert, util) {
+ var map;
+ var mapping;
+
+ map = new SourceMapConsumer(util.testMap);
+
+ mapping = map.originalPositionFor({
+ line: 2,
+ column: 1
+ });
+ assert.equal(mapping.source, '/the/root/two.js');
+
+ mapping = map.originalPositionFor({
+ line: 1,
+ column: 1
+ });
+ assert.equal(mapping.source, '/the/root/one.js');
+
+
+ map = new SourceMapConsumer(util.testMapNoSourceRoot);
+
+ mapping = map.originalPositionFor({
+ line: 2,
+ column: 1
+ });
+ assert.equal(mapping.source, 'two.js');
+
+ mapping = map.originalPositionFor({
+ line: 1,
+ column: 1
+ });
+ assert.equal(mapping.source, 'one.js');
+
+
+ map = new SourceMapConsumer(util.testMapEmptySourceRoot);
+
+ mapping = map.originalPositionFor({
+ line: 2,
+ column: 1
+ });
+ assert.equal(mapping.source, 'two.js');
+
+ mapping = map.originalPositionFor({
+ line: 1,
+ column: 1
+ });
+ assert.equal(mapping.source, 'one.js');
+ };
+
+ exports['test mapping tokens back exactly'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMap);
+
+ util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
+ util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
+ util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
+ util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
+ util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
+ util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
+ util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
+
+ util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
+ util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
+ util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
+ util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
+ util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
+ util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
+ };
+
+ exports['test mapping tokens back exactly in indexed source map'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.indexedTestMap);
+
+ util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
+ util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
+ util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
+ util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
+ util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
+ util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
+ util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
+
+ util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
+ util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
+ util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
+ util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
+ util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
+ util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
+ };
+
+
+ exports['test mapping tokens back exactly'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMap);
+
+ util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
+ util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
+ util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
+ util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
+ util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
+ util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
+ util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
+
+ util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
+ util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
+ util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
+ util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
+ util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
+ util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
+ };
+
+ exports['test mapping tokens fuzzy'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMap);
+
+ // Finding original positions
+ util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', map, assert, true);
+ util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', map, assert, true);
+ util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, map, assert, true);
+
+ // Finding generated positions
+ util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', map, assert, null, true);
+ util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', map, assert, null, true);
+ util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, map, assert, null, true);
+ };
+
+ exports['test mapping tokens fuzzy in indexed source map'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.indexedTestMap);
+
+ // Finding original positions
+ util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', map, assert, true);
+ util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', map, assert, true);
+ util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, map, assert, true);
+
+ // Finding generated positions
+ util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', map, assert, null, true);
+ util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', map, assert, null, true);
+ util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, map, assert, null, true);
+ };
+
+ exports['test mappings and end of lines'] = function (assert, util) {
+ var smg = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ smg.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 1, column: 1 },
+ source: 'bar.js'
+ });
+ smg.addMapping({
+ original: { line: 2, column: 2 },
+ generated: { line: 2, column: 2 },
+ source: 'bar.js'
+ });
+
+ var map = SourceMapConsumer.fromSourceMap(smg);
+
+ // When finding original positions, mappings end at the end of the line.
+ util.assertMapping(2, 1, null, null, null, null, map, assert, true)
+
+ // When finding generated positions, mappings do not end at the end of the line.
+ util.assertMapping(1, 1, 'bar.js', 2, 1, null, map, assert, null, true);
+ };
+
+ exports['test creating source map consumers with )]}\' prefix'] = function (assert, util) {
+ assert.doesNotThrow(function () {
+ var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
+ });
+ };
+
+ exports['test eachMapping'] = function (assert, util) {
+ var map;
+
+ map = new SourceMapConsumer(util.testMap);
+ var previousLine = -Infinity;
+ var previousColumn = -Infinity;
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.generatedLine >= previousLine);
+
+ assert.ok(mapping.source === '/the/root/one.js' || mapping.source === '/the/root/two.js');
+
+ if (mapping.generatedLine === previousLine) {
+ assert.ok(mapping.generatedColumn >= previousColumn);
+ previousColumn = mapping.generatedColumn;
+ }
+ else {
+ previousLine = mapping.generatedLine;
+ previousColumn = -Infinity;
+ }
+ });
+
+ map = new SourceMapConsumer(util.testMapNoSourceRoot);
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.source === 'one.js' || mapping.source === 'two.js');
+ });
+
+ map = new SourceMapConsumer(util.testMapEmptySourceRoot);
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.source === 'one.js' || mapping.source === 'two.js');
+ });
+ };
+
+ exports['test eachMapping for indexed source maps'] = function(assert, util) {
+ var map = new SourceMapConsumer(util.indexedTestMap);
+ var previousLine = -Infinity;
+ var previousColumn = -Infinity;
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.generatedLine >= previousLine);
+
+ if (mapping.source) {
+ assert.equal(mapping.source.indexOf(util.testMap.sourceRoot), 0);
+ }
+
+ if (mapping.generatedLine === previousLine) {
+ assert.ok(mapping.generatedColumn >= previousColumn);
+ previousColumn = mapping.generatedColumn;
+ }
+ else {
+ previousLine = mapping.generatedLine;
+ previousColumn = -Infinity;
+ }
+ });
+ };
+
+
+ exports['test iterating over mappings in a different order'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMap);
+ var previousLine = -Infinity;
+ var previousColumn = -Infinity;
+ var previousSource = "";
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.source >= previousSource);
+
+ if (mapping.source === previousSource) {
+ assert.ok(mapping.originalLine >= previousLine);
+
+ if (mapping.originalLine === previousLine) {
+ assert.ok(mapping.originalColumn >= previousColumn);
+ previousColumn = mapping.originalColumn;
+ }
+ else {
+ previousLine = mapping.originalLine;
+ previousColumn = -Infinity;
+ }
+ }
+ else {
+ previousSource = mapping.source;
+ previousLine = -Infinity;
+ previousColumn = -Infinity;
+ }
+ }, null, SourceMapConsumer.ORIGINAL_ORDER);
+ };
+
+ exports['test iterating over mappings in a different order in indexed source maps'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.indexedTestMap);
+ var previousLine = -Infinity;
+ var previousColumn = -Infinity;
+ var previousSource = "";
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.source >= previousSource);
+
+ if (mapping.source === previousSource) {
+ assert.ok(mapping.originalLine >= previousLine);
+
+ if (mapping.originalLine === previousLine) {
+ assert.ok(mapping.originalColumn >= previousColumn);
+ previousColumn = mapping.originalColumn;
+ }
+ else {
+ previousLine = mapping.originalLine;
+ previousColumn = -Infinity;
+ }
+ }
+ else {
+ previousSource = mapping.source;
+ previousLine = -Infinity;
+ previousColumn = -Infinity;
+ }
+ }, null, SourceMapConsumer.ORIGINAL_ORDER);
+ };
+
+ exports['test that we can set the context for `this` in eachMapping'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMap);
+ var context = {};
+ map.eachMapping(function () {
+ assert.equal(this, context);
+ }, context);
+ };
+
+ exports['test that we can set the context for `this` in eachMapping in indexed source maps'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.indexedTestMap);
+ var context = {};
+ map.eachMapping(function () {
+ assert.equal(this, context);
+ }, context);
+ };
+
+ exports['test that the `sourcesContent` field has the original sources'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMapWithSourcesContent);
+ var sourcesContent = map.sourcesContent;
+
+ assert.equal(sourcesContent[0], ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(sourcesContent[1], ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.equal(sourcesContent.length, 2);
+ };
+
+ exports['test that we can get the original sources for the sources'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMapWithSourcesContent);
+ var sources = map.sources;
+
+ assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.throws(function () {
+ map.sourceContentFor("");
+ }, Error);
+ assert.throws(function () {
+ map.sourceContentFor("/the/root/three.js");
+ }, Error);
+ assert.throws(function () {
+ map.sourceContentFor("three.js");
+ }, Error);
+ };
+
+ exports['test that we can get the original source content with relative source paths'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.testMapRelativeSources);
+ var sources = map.sources;
+
+ assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.throws(function () {
+ map.sourceContentFor("");
+ }, Error);
+ assert.throws(function () {
+ map.sourceContentFor("/the/root/three.js");
+ }, Error);
+ assert.throws(function () {
+ map.sourceContentFor("three.js");
+ }, Error);
+ };
+
+ exports['test that we can get the original source content for the sources on an indexed source map'] = function (assert, util) {
+ var map = new SourceMapConsumer(util.indexedTestMap);
+ var sources = map.sources;
+
+ assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
+ assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };');
+ assert.throws(function () {
+ map.sourceContentFor("");
+ }, Error);
+ assert.throws(function () {
+ map.sourceContentFor("/the/root/three.js");
+ }, Error);
+ assert.throws(function () {
+ map.sourceContentFor("three.js");
+ }, Error);
+ };
+
+
+ exports['test sourceRoot + generatedPositionFor'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ sourceRoot: 'foo/bar',
+ file: 'baz.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'bang.coffee'
+ });
+ map.addMapping({
+ original: { line: 5, column: 5 },
+ generated: { line: 6, column: 6 },
+ source: 'bang.coffee'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ // Should handle without sourceRoot.
+ var pos = map.generatedPositionFor({
+ line: 1,
+ column: 1,
+ source: 'bang.coffee'
+ });
+
+ assert.equal(pos.line, 2);
+ assert.equal(pos.column, 2);
+
+ // Should handle with sourceRoot.
+ var pos = map.generatedPositionFor({
+ line: 1,
+ column: 1,
+ source: 'foo/bar/bang.coffee'
+ });
+
+ assert.equal(pos.line, 2);
+ assert.equal(pos.column, 2);
+ };
+
+ exports['test allGeneratedPositionsFor'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'bar.coffee'
+ });
+ map.addMapping({
+ original: { line: 2, column: 1 },
+ generated: { line: 3, column: 2 },
+ source: 'bar.coffee'
+ });
+ map.addMapping({
+ original: { line: 2, column: 2 },
+ generated: { line: 3, column: 3 },
+ source: 'bar.coffee'
+ });
+ map.addMapping({
+ original: { line: 3, column: 1 },
+ generated: { line: 4, column: 2 },
+ source: 'bar.coffee'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var mappings = map.allGeneratedPositionsFor({
+ line: 2,
+ source: 'bar.coffee'
+ });
+
+ assert.equal(mappings.length, 2);
+ assert.equal(mappings[0].line, 3);
+ assert.equal(mappings[0].column, 2);
+ assert.equal(mappings[1].line, 3);
+ assert.equal(mappings[1].column, 3);
+ };
+
+ exports['test allGeneratedPositionsFor for line with no mappings'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'bar.coffee'
+ });
+ map.addMapping({
+ original: { line: 3, column: 1 },
+ generated: { line: 4, column: 2 },
+ source: 'bar.coffee'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var mappings = map.allGeneratedPositionsFor({
+ line: 2,
+ source: 'bar.coffee'
+ });
+
+ assert.equal(mappings.length, 0);
+ };
+
+ exports['test allGeneratedPositionsFor source map with no mappings'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated.js'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var mappings = map.allGeneratedPositionsFor({
+ line: 2,
+ source: 'bar.coffee'
+ });
+
+ assert.equal(mappings.length, 0);
+ };
+
+ exports['test computeColumnSpans'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 1, column: 1 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 2, column: 1 },
+ generated: { line: 2, column: 1 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 2, column: 2 },
+ generated: { line: 2, column: 10 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 2, column: 3 },
+ generated: { line: 2, column: 20 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 3, column: 1 },
+ generated: { line: 3, column: 1 },
+ source: 'foo.coffee'
+ });
+ map.addMapping({
+ original: { line: 3, column: 2 },
+ generated: { line: 3, column: 2 },
+ source: 'foo.coffee'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ map.computeColumnSpans();
+
+ var mappings = map.allGeneratedPositionsFor({
+ line: 1,
+ source: 'foo.coffee'
+ });
+
+ assert.equal(mappings.length, 1);
+ assert.equal(mappings[0].lastColumn, Infinity);
+
+ var mappings = map.allGeneratedPositionsFor({
+ line: 2,
+ source: 'foo.coffee'
+ });
+
+ assert.equal(mappings.length, 3);
+ assert.equal(mappings[0].lastColumn, 9);
+ assert.equal(mappings[1].lastColumn, 19);
+ assert.equal(mappings[2].lastColumn, Infinity);
+
+ var mappings = map.allGeneratedPositionsFor({
+ line: 3,
+ source: 'foo.coffee'
+ });
+
+ assert.equal(mappings.length, 2);
+ assert.equal(mappings[0].lastColumn, 1);
+ assert.equal(mappings[1].lastColumn, Infinity);
+ };
+
+ exports['test sourceRoot + originalPositionFor'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ sourceRoot: 'foo/bar',
+ file: 'baz.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'bang.coffee'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var pos = map.originalPositionFor({
+ line: 2,
+ column: 2,
+ });
+
+ // Should always have the prepended source root
+ assert.equal(pos.source, 'foo/bar/bang.coffee');
+ assert.equal(pos.line, 1);
+ assert.equal(pos.column, 1);
+ };
+
+ exports['test github issue #56'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ sourceRoot: 'http://',
+ file: 'www.example.com/foo.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'www.example.com/original.js'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var sources = map.sources;
+ assert.equal(sources.length, 1);
+ assert.equal(sources[0], 'http://www.example.com/original.js');
+ };
+
+ exports['test github issue #43'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ sourceRoot: 'http://example.com',
+ file: 'foo.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'http://cdn.example.com/original.js'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var sources = map.sources;
+ assert.equal(sources.length, 1,
+ 'Should only be one source.');
+ assert.equal(sources[0], 'http://cdn.example.com/original.js',
+ 'Should not be joined with the sourceRoot.');
+ };
+
+ exports['test absolute path, but same host sources'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ sourceRoot: 'http://example.com/foo/bar',
+ file: 'foo.js'
+ });
+ map.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: '/original.js'
+ });
+ map = new SourceMapConsumer(map.toString());
+
+ var sources = map.sources;
+ assert.equal(sources.length, 1,
+ 'Should only be one source.');
+ assert.equal(sources[0], 'http://example.com/original.js',
+ 'Source should be relative the host of the source root.');
+ };
+
+ exports['test indexed source map errors when sections are out of order by line'] = function(assert, util) {
+ // Make a deep copy of the indexedTestMap
+ var misorderedIndexedTestMap = JSON.parse(JSON.stringify(util.indexedTestMap));
+
+ misorderedIndexedTestMap.sections[0].offset = {
+ line: 2,
+ column: 0
+ };
+
+ assert.throws(function() {
+ new SourceMapConsumer(misorderedIndexedTestMap);
+ }, Error);
+ };
+
+ exports['test github issue #64'] = function (assert, util) {
+ var map = new SourceMapConsumer({
+ "version": 3,
+ "file": "foo.js",
+ "sourceRoot": "http://example.com/",
+ "sources": ["/a"],
+ "names": [],
+ "mappings": "AACA",
+ "sourcesContent": ["foo"]
+ });
+
+ assert.equal(map.sourceContentFor("a"), "foo");
+ assert.equal(map.sourceContentFor("/a"), "foo");
+ };
+
+ exports['test bug 885597'] = function (assert, util) {
+ var map = new SourceMapConsumer({
+ "version": 3,
+ "file": "foo.js",
+ "sourceRoot": "file:///Users/AlGore/Invented/The/Internet/",
+ "sources": ["/a"],
+ "names": [],
+ "mappings": "AACA",
+ "sourcesContent": ["foo"]
+ });
+
+ var s = map.sources[0];
+ assert.equal(map.sourceContentFor(s), "foo");
+ };
+
+ exports['test github issue #72, duplicate sources'] = function (assert, util) {
+ var map = new SourceMapConsumer({
+ "version": 3,
+ "file": "foo.js",
+ "sources": ["source1.js", "source1.js", "source3.js"],
+ "names": [],
+ "mappings": ";EAAC;;IAEE;;MEEE",
+ "sourceRoot": "http://example.com"
+ });
+
+ var pos = map.originalPositionFor({
+ line: 2,
+ column: 2
+ });
+ assert.equal(pos.source, 'http://example.com/source1.js');
+ assert.equal(pos.line, 1);
+ assert.equal(pos.column, 1);
+
+ var pos = map.originalPositionFor({
+ line: 4,
+ column: 4
+ });
+ assert.equal(pos.source, 'http://example.com/source1.js');
+ assert.equal(pos.line, 3);
+ assert.equal(pos.column, 3);
+
+ var pos = map.originalPositionFor({
+ line: 6,
+ column: 6
+ });
+ assert.equal(pos.source, 'http://example.com/source3.js');
+ assert.equal(pos.line, 5);
+ assert.equal(pos.column, 5);
+ };
+
+ exports['test github issue #72, duplicate names'] = function (assert, util) {
+ var map = new SourceMapConsumer({
+ "version": 3,
+ "file": "foo.js",
+ "sources": ["source.js"],
+ "names": ["name1", "name1", "name3"],
+ "mappings": ";EAACA;;IAEEA;;MAEEE",
+ "sourceRoot": "http://example.com"
+ });
+
+ var pos = map.originalPositionFor({
+ line: 2,
+ column: 2
+ });
+ assert.equal(pos.name, 'name1');
+ assert.equal(pos.line, 1);
+ assert.equal(pos.column, 1);
+
+ var pos = map.originalPositionFor({
+ line: 4,
+ column: 4
+ });
+ assert.equal(pos.name, 'name1');
+ assert.equal(pos.line, 3);
+ assert.equal(pos.column, 3);
+
+ var pos = map.originalPositionFor({
+ line: 6,
+ column: 6
+ });
+ assert.equal(pos.name, 'name3');
+ assert.equal(pos.line, 5);
+ assert.equal(pos.column, 5);
+ };
+
+ exports['test SourceMapConsumer.fromSourceMap'] = function (assert, util) {
+ var smg = new SourceMapGenerator({
+ sourceRoot: 'http://example.com/',
+ file: 'foo.js'
+ });
+ smg.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'bar.js'
+ });
+ smg.addMapping({
+ original: { line: 2, column: 2 },
+ generated: { line: 4, column: 4 },
+ source: 'baz.js',
+ name: 'dirtMcGirt'
+ });
+ smg.setSourceContent('baz.js', 'baz.js content');
+
+ var smc = SourceMapConsumer.fromSourceMap(smg);
+ assert.equal(smc.file, 'foo.js');
+ assert.equal(smc.sourceRoot, 'http://example.com/');
+ assert.equal(smc.sources.length, 2);
+ assert.equal(smc.sources[0], 'http://example.com/bar.js');
+ assert.equal(smc.sources[1], 'http://example.com/baz.js');
+ assert.equal(smc.sourceContentFor('baz.js'), 'baz.js content');
+
+ var pos = smc.originalPositionFor({
+ line: 2,
+ column: 2
+ });
+ assert.equal(pos.line, 1);
+ assert.equal(pos.column, 1);
+ assert.equal(pos.source, 'http://example.com/bar.js');
+ assert.equal(pos.name, null);
+
+ pos = smc.generatedPositionFor({
+ line: 1,
+ column: 1,
+ source: 'http://example.com/bar.js'
+ });
+ assert.equal(pos.line, 2);
+ assert.equal(pos.column, 2);
+
+ pos = smc.originalPositionFor({
+ line: 4,
+ column: 4
+ });
+ assert.equal(pos.line, 2);
+ assert.equal(pos.column, 2);
+ assert.equal(pos.source, 'http://example.com/baz.js');
+ assert.equal(pos.name, 'dirtMcGirt');
+
+ pos = smc.generatedPositionFor({
+ line: 2,
+ column: 2,
+ source: 'http://example.com/baz.js'
+ });
+ assert.equal(pos.line, 4);
+ assert.equal(pos.column, 4);
+ };
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+ var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+ var SourceNode = require('../../lib/source-map/source-node').SourceNode;
+ var util = require('./util');
+
+ exports['test some simple stuff'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'foo.js',
+ sourceRoot: '.'
+ });
+ assert.ok(true);
+
+ var map = new SourceMapGenerator().toJSON();
+ assert.ok(!('file' in map));
+ assert.ok(!('sourceRoot' in map));
+ };
+
+ exports['test JSON serialization'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'foo.js',
+ sourceRoot: '.'
+ });
+ assert.equal(map.toString(), JSON.stringify(map));
+ };
+
+ exports['test adding mappings (case 1)'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.'
+ });
+
+ assert.doesNotThrow(function () {
+ map.addMapping({
+ generated: { line: 1, column: 1 }
+ });
+ });
+ };
+
+ exports['test adding mappings (case 2)'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.'
+ });
+
+ assert.doesNotThrow(function () {
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ source: 'bar.js',
+ original: { line: 1, column: 1 }
+ });
+ });
+ };
+
+ exports['test adding mappings (case 3)'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.'
+ });
+
+ assert.doesNotThrow(function () {
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ source: 'bar.js',
+ original: { line: 1, column: 1 },
+ name: 'someToken'
+ });
+ });
+ };
+
+ exports['test adding mappings (invalid)'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.'
+ });
+
+ // Not enough info.
+ assert.throws(function () {
+ map.addMapping({});
+ });
+
+ // Original file position, but no source.
+ assert.throws(function () {
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 1, column: 1 }
+ });
+ });
+ };
+
+ exports['test adding mappings with skipValidation'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.',
+ skipValidation: true
+ });
+
+ // Not enough info, caught by `util.getArgs`
+ assert.throws(function () {
+ map.addMapping({});
+ });
+
+ // Original file position, but no source. Not checked.
+ assert.doesNotThrow(function () {
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 1, column: 1 }
+ });
+ });
+ };
+
+ exports['test that the correct mappings are being generated'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'min.js',
+ sourceRoot: '/the/root'
+ });
+
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 1, column: 1 },
+ source: 'one.js'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 5 },
+ original: { line: 1, column: 5 },
+ source: 'one.js'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 9 },
+ original: { line: 1, column: 11 },
+ source: 'one.js'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 18 },
+ original: { line: 1, column: 21 },
+ source: 'one.js',
+ name: 'bar'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 21 },
+ original: { line: 2, column: 3 },
+ source: 'one.js'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 28 },
+ original: { line: 2, column: 10 },
+ source: 'one.js',
+ name: 'baz'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 32 },
+ original: { line: 2, column: 14 },
+ source: 'one.js',
+ name: 'bar'
+ });
+
+ map.addMapping({
+ generated: { line: 2, column: 1 },
+ original: { line: 1, column: 1 },
+ source: 'two.js'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 5 },
+ original: { line: 1, column: 5 },
+ source: 'two.js'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 9 },
+ original: { line: 1, column: 11 },
+ source: 'two.js'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 18 },
+ original: { line: 1, column: 21 },
+ source: 'two.js',
+ name: 'n'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 21 },
+ original: { line: 2, column: 3 },
+ source: 'two.js'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 28 },
+ original: { line: 2, column: 10 },
+ source: 'two.js',
+ name: 'n'
+ });
+
+ map = JSON.parse(map.toString());
+
+ util.assertEqualMaps(assert, map, util.testMap);
+ };
+
+ exports['test that adding a mapping with an empty string name does not break generation'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.'
+ });
+
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ source: 'bar.js',
+ original: { line: 1, column: 1 },
+ name: ''
+ });
+
+ assert.doesNotThrow(function () {
+ JSON.parse(map.toString());
+ });
+ };
+
+ exports['test that source content can be set'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'min.js',
+ sourceRoot: '/the/root'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 1, column: 1 },
+ source: 'one.js'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 1 },
+ original: { line: 1, column: 1 },
+ source: 'two.js'
+ });
+ map.setSourceContent('one.js', 'one file content');
+
+ map = JSON.parse(map.toString());
+ assert.equal(map.sources[0], 'one.js');
+ assert.equal(map.sources[1], 'two.js');
+ assert.equal(map.sourcesContent[0], 'one file content');
+ assert.equal(map.sourcesContent[1], null);
+ };
+
+ exports['test .fromSourceMap'] = function (assert, util) {
+ var map = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(util.testMap));
+ util.assertEqualMaps(assert, map.toJSON(), util.testMap);
+ };
+
+ exports['test .fromSourceMap with sourcesContent'] = function (assert, util) {
+ var map = SourceMapGenerator.fromSourceMap(
+ new SourceMapConsumer(util.testMapWithSourcesContent));
+ util.assertEqualMaps(assert, map.toJSON(), util.testMapWithSourcesContent);
+ };
+
+ exports['test applySourceMap'] = function (assert, util) {
+ var node = new SourceNode(null, null, null, [
+ new SourceNode(2, 0, 'fileX', 'lineX2\n'),
+ 'genA1\n',
+ new SourceNode(2, 0, 'fileY', 'lineY2\n'),
+ 'genA2\n',
+ new SourceNode(1, 0, 'fileX', 'lineX1\n'),
+ 'genA3\n',
+ new SourceNode(1, 0, 'fileY', 'lineY1\n')
+ ]);
+ var mapStep1 = node.toStringWithSourceMap({
+ file: 'fileA'
+ }).map;
+ mapStep1.setSourceContent('fileX', 'lineX1\nlineX2\n');
+ mapStep1 = mapStep1.toJSON();
+
+ node = new SourceNode(null, null, null, [
+ 'gen1\n',
+ new SourceNode(1, 0, 'fileA', 'lineA1\n'),
+ new SourceNode(2, 0, 'fileA', 'lineA2\n'),
+ new SourceNode(3, 0, 'fileA', 'lineA3\n'),
+ new SourceNode(4, 0, 'fileA', 'lineA4\n'),
+ new SourceNode(1, 0, 'fileB', 'lineB1\n'),
+ new SourceNode(2, 0, 'fileB', 'lineB2\n'),
+ 'gen2\n'
+ ]);
+ var mapStep2 = node.toStringWithSourceMap({
+ file: 'fileGen'
+ }).map;
+ mapStep2.setSourceContent('fileB', 'lineB1\nlineB2\n');
+ mapStep2 = mapStep2.toJSON();
+
+ node = new SourceNode(null, null, null, [
+ 'gen1\n',
+ new SourceNode(2, 0, 'fileX', 'lineA1\n'),
+ new SourceNode(2, 0, 'fileA', 'lineA2\n'),
+ new SourceNode(2, 0, 'fileY', 'lineA3\n'),
+ new SourceNode(4, 0, 'fileA', 'lineA4\n'),
+ new SourceNode(1, 0, 'fileB', 'lineB1\n'),
+ new SourceNode(2, 0, 'fileB', 'lineB2\n'),
+ 'gen2\n'
+ ]);
+ var expectedMap = node.toStringWithSourceMap({
+ file: 'fileGen'
+ }).map;
+ expectedMap.setSourceContent('fileX', 'lineX1\nlineX2\n');
+ expectedMap.setSourceContent('fileB', 'lineB1\nlineB2\n');
+ expectedMap = expectedMap.toJSON();
+
+ // apply source map "mapStep1" to "mapStep2"
+ var generator = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(mapStep2));
+ generator.applySourceMap(new SourceMapConsumer(mapStep1));
+ var actualMap = generator.toJSON();
+
+ util.assertEqualMaps(assert, actualMap, expectedMap);
+ };
+
+ exports['test applySourceMap throws when file is missing'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'test.js'
+ });
+ var map2 = new SourceMapGenerator();
+ assert.throws(function() {
+ map.applySourceMap(new SourceMapConsumer(map2.toJSON()));
+ });
+ };
+
+ exports['test the two additional parameters of applySourceMap'] = function (assert, util) {
+ // Assume the following directory structure:
+ //
+ // http://foo.org/
+ // bar.coffee
+ // app/
+ // coffee/
+ // foo.coffee
+ // temp/
+ // bundle.js
+ // temp_maps/
+ // bundle.js.map
+ // public/
+ // bundle.min.js
+ // bundle.min.js.map
+ //
+ // http://www.example.com/
+ // baz.coffee
+
+ var bundleMap = new SourceMapGenerator({
+ file: 'bundle.js'
+ });
+ bundleMap.addMapping({
+ generated: { line: 3, column: 3 },
+ original: { line: 2, column: 2 },
+ source: '../../coffee/foo.coffee'
+ });
+ bundleMap.setSourceContent('../../coffee/foo.coffee', 'foo coffee');
+ bundleMap.addMapping({
+ generated: { line: 13, column: 13 },
+ original: { line: 12, column: 12 },
+ source: '/bar.coffee'
+ });
+ bundleMap.setSourceContent('/bar.coffee', 'bar coffee');
+ bundleMap.addMapping({
+ generated: { line: 23, column: 23 },
+ original: { line: 22, column: 22 },
+ source: 'http://www.example.com/baz.coffee'
+ });
+ bundleMap.setSourceContent(
+ 'http://www.example.com/baz.coffee',
+ 'baz coffee'
+ );
+ bundleMap = new SourceMapConsumer(bundleMap.toJSON());
+
+ var minifiedMap = new SourceMapGenerator({
+ file: 'bundle.min.js',
+ sourceRoot: '..'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 3, column: 3 },
+ source: 'temp/bundle.js'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 11, column: 11 },
+ original: { line: 13, column: 13 },
+ source: 'temp/bundle.js'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 21, column: 21 },
+ original: { line: 23, column: 23 },
+ source: 'temp/bundle.js'
+ });
+ minifiedMap = new SourceMapConsumer(minifiedMap.toJSON());
+
+ var expectedMap = function (sources) {
+ var map = new SourceMapGenerator({
+ file: 'bundle.min.js',
+ sourceRoot: '..'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 2, column: 2 },
+ source: sources[0]
+ });
+ map.setSourceContent(sources[0], 'foo coffee');
+ map.addMapping({
+ generated: { line: 11, column: 11 },
+ original: { line: 12, column: 12 },
+ source: sources[1]
+ });
+ map.setSourceContent(sources[1], 'bar coffee');
+ map.addMapping({
+ generated: { line: 21, column: 21 },
+ original: { line: 22, column: 22 },
+ source: sources[2]
+ });
+ map.setSourceContent(sources[2], 'baz coffee');
+ return map.toJSON();
+ }
+
+ var actualMap = function (aSourceMapPath) {
+ var map = SourceMapGenerator.fromSourceMap(minifiedMap);
+ // Note that relying on `bundleMap.file` (which is simply 'bundle.js')
+ // instead of supplying the second parameter wouldn't work here.
+ map.applySourceMap(bundleMap, '../temp/bundle.js', aSourceMapPath);
+ return map.toJSON();
+ }
+
+ util.assertEqualMaps(assert, actualMap('../temp/temp_maps'), expectedMap([
+ 'coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('/app/temp/temp_maps'), expectedMap([
+ '/app/coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('http://foo.org/app/temp/temp_maps'), expectedMap([
+ 'http://foo.org/app/coffee/foo.coffee',
+ 'http://foo.org/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ // If the third parameter is omitted or set to the current working
+ // directory we get incorrect source paths:
+
+ util.assertEqualMaps(assert, actualMap(), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap(''), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('.'), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('./'), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+ };
+
+ exports['test applySourceMap name handling'] = function (assert, util) {
+ // Imagine some CoffeeScript code being compiled into JavaScript and then
+ // minified.
+
+ var assertName = function(coffeeName, jsName, expectedName) {
+ var minifiedMap = new SourceMapGenerator({
+ file: 'test.js.min'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 1, column: 4 },
+ original: { line: 1, column: 4 },
+ source: 'test.js',
+ name: jsName
+ });
+
+ var coffeeMap = new SourceMapGenerator({
+ file: 'test.js'
+ });
+ coffeeMap.addMapping({
+ generated: { line: 1, column: 4 },
+ original: { line: 1, column: 0 },
+ source: 'test.coffee',
+ name: coffeeName
+ });
+
+ minifiedMap.applySourceMap(new SourceMapConsumer(coffeeMap.toJSON()));
+
+ new SourceMapConsumer(minifiedMap.toJSON()).eachMapping(function(mapping) {
+ assert.equal(mapping.name, expectedName);
+ });
+ };
+
+ // `foo = 1` -> `var foo = 1;` -> `var a=1`
+ // CoffeeScript doesn’t rename variables, so there’s no need for it to
+ // provide names in its source maps. Minifiers do rename variables and
+ // therefore do provide names in their source maps. So that name should be
+ // retained if the original map lacks names.
+ assertName(null, 'foo', 'foo');
+
+ // `foo = 1` -> `var coffee$foo = 1;` -> `var a=1`
+ // Imagine that CoffeeScript prefixed all variables with `coffee$`. Even
+ // though the minifier then also provides a name, the original name is
+ // what corresponds to the source.
+ assertName('foo', 'coffee$foo', 'foo');
+
+ // `foo = 1` -> `var coffee$foo = 1;` -> `var coffee$foo=1`
+ // Minifiers can turn off variable mangling. Then there’s no need to
+ // provide names in the source map, but the names from the original map are
+ // still needed.
+ assertName('foo', null, 'foo');
+
+ // `foo = 1` -> `var foo = 1;` -> `var foo=1`
+ // No renaming at all.
+ assertName(null, null, null);
+ };
+
+ exports['test sorting with duplicate generated mappings'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'test.js'
+ });
+ map.addMapping({
+ generated: { line: 3, column: 0 },
+ original: { line: 2, column: 0 },
+ source: 'a.js'
+ });
+ map.addMapping({
+ generated: { line: 2, column: 0 }
+ });
+ map.addMapping({
+ generated: { line: 2, column: 0 }
+ });
+ map.addMapping({
+ generated: { line: 1, column: 0 },
+ original: { line: 1, column: 0 },
+ source: 'a.js'
+ });
+
+ util.assertEqualMaps(assert, map.toJSON(), {
+ version: 3,
+ file: 'test.js',
+ sources: ['a.js'],
+ names: [],
+ mappings: 'AAAA;A;AACA'
+ });
+ };
+
+ exports['test ignore duplicate mappings.'] = function (assert, util) {
+ var init = { file: 'min.js', sourceRoot: '/the/root' };
+ var map1, map2;
+
+ // null original source location
+ var nullMapping1 = {
+ generated: { line: 1, column: 0 }
+ };
+ var nullMapping2 = {
+ generated: { line: 2, column: 2 }
+ };
+
+ map1 = new SourceMapGenerator(init);
+ map2 = new SourceMapGenerator(init);
+
+ map1.addMapping(nullMapping1);
+ map1.addMapping(nullMapping1);
+
+ map2.addMapping(nullMapping1);
+
+ util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+ map1.addMapping(nullMapping2);
+ map1.addMapping(nullMapping1);
+
+ map2.addMapping(nullMapping2);
+
+ util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+ // original source location
+ var srcMapping1 = {
+ generated: { line: 1, column: 0 },
+ original: { line: 11, column: 0 },
+ source: 'srcMapping1.js'
+ };
+ var srcMapping2 = {
+ generated: { line: 2, column: 2 },
+ original: { line: 11, column: 0 },
+ source: 'srcMapping2.js'
+ };
+
+ map1 = new SourceMapGenerator(init);
+ map2 = new SourceMapGenerator(init);
+
+ map1.addMapping(srcMapping1);
+ map1.addMapping(srcMapping1);
+
+ map2.addMapping(srcMapping1);
+
+ util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+ map1.addMapping(srcMapping2);
+ map1.addMapping(srcMapping1);
+
+ map2.addMapping(srcMapping2);
+
+ util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+ // full original source and name information
+ var fullMapping1 = {
+ generated: { line: 1, column: 0 },
+ original: { line: 11, column: 0 },
+ source: 'fullMapping1.js',
+ name: 'fullMapping1'
+ };
+ var fullMapping2 = {
+ generated: { line: 2, column: 2 },
+ original: { line: 11, column: 0 },
+ source: 'fullMapping2.js',
+ name: 'fullMapping2'
+ };
+
+ map1 = new SourceMapGenerator(init);
+ map2 = new SourceMapGenerator(init);
+
+ map1.addMapping(fullMapping1);
+ map1.addMapping(fullMapping1);
+
+ map2.addMapping(fullMapping1);
+
+ util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+ map1.addMapping(fullMapping2);
+ map1.addMapping(fullMapping1);
+
+ map2.addMapping(fullMapping2);
+
+ util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+ };
+
+ exports['test github issue #72, check for duplicate names or sources'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'test.js'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 2, column: 2 },
+ source: 'a.js',
+ name: 'foo'
+ });
+ map.addMapping({
+ generated: { line: 3, column: 3 },
+ original: { line: 4, column: 4 },
+ source: 'a.js',
+ name: 'foo'
+ });
+ util.assertEqualMaps(assert, map.toJSON(), {
+ version: 3,
+ file: 'test.js',
+ sources: ['a.js'],
+ names: ['foo'],
+ mappings: 'CACEA;;GAEEA'
+ });
+ };
+
+ exports['test setting sourcesContent to null when already null'] = function (assert, util) {
+ var smg = new SourceMapGenerator({ file: "foo.js" });
+ assert.doesNotThrow(function() {
+ smg.setSourceContent("bar.js", null);
+ });
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+ var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+ var SourceNode = require('../../lib/source-map/source-node').SourceNode;
+
+ function forEachNewline(fn) {
+ return function (assert, util) {
+ ['\n', '\r\n'].forEach(fn.bind(null, assert, util));
+ }
+ }
+
+ exports['test .add()'] = function (assert, util) {
+ var node = new SourceNode(null, null, null);
+
+ // Adding a string works.
+ node.add('function noop() {}');
+
+ // Adding another source node works.
+ node.add(new SourceNode(null, null, null));
+
+ // Adding an array works.
+ node.add(['function foo() {',
+ new SourceNode(null, null, null,
+ 'return 10;'),
+ '}']);
+
+ // Adding other stuff doesn't.
+ assert.throws(function () {
+ node.add({});
+ });
+ assert.throws(function () {
+ node.add(function () {});
+ });
+ };
+
+ exports['test .prepend()'] = function (assert, util) {
+ var node = new SourceNode(null, null, null);
+
+ // Prepending a string works.
+ node.prepend('function noop() {}');
+ assert.equal(node.children[0], 'function noop() {}');
+ assert.equal(node.children.length, 1);
+
+ // Prepending another source node works.
+ node.prepend(new SourceNode(null, null, null));
+ assert.equal(node.children[0], '');
+ assert.equal(node.children[1], 'function noop() {}');
+ assert.equal(node.children.length, 2);
+
+ // Prepending an array works.
+ node.prepend(['function foo() {',
+ new SourceNode(null, null, null,
+ 'return 10;'),
+ '}']);
+ assert.equal(node.children[0], 'function foo() {');
+ assert.equal(node.children[1], 'return 10;');
+ assert.equal(node.children[2], '}');
+ assert.equal(node.children[3], '');
+ assert.equal(node.children[4], 'function noop() {}');
+ assert.equal(node.children.length, 5);
+
+ // Prepending other stuff doesn't.
+ assert.throws(function () {
+ node.prepend({});
+ });
+ assert.throws(function () {
+ node.prepend(function () {});
+ });
+ };
+
+ exports['test .toString()'] = function (assert, util) {
+ assert.equal((new SourceNode(null, null, null,
+ ['function foo() {',
+ new SourceNode(null, null, null, 'return 10;'),
+ '}'])).toString(),
+ 'function foo() {return 10;}');
+ };
+
+ exports['test .join()'] = function (assert, util) {
+ assert.equal((new SourceNode(null, null, null,
+ ['a', 'b', 'c', 'd'])).join(', ').toString(),
+ 'a, b, c, d');
+ };
+
+ exports['test .walk()'] = function (assert, util) {
+ var node = new SourceNode(null, null, null,
+ ['(function () {\n',
+ ' ', new SourceNode(1, 0, 'a.js', ['someCall()']), ';\n',
+ ' ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+ '}());']);
+ var expected = [
+ { str: '(function () {\n', source: null, line: null, column: null },
+ { str: ' ', source: null, line: null, column: null },
+ { str: 'someCall()', source: 'a.js', line: 1, column: 0 },
+ { str: ';\n', source: null, line: null, column: null },
+ { str: ' ', source: null, line: null, column: null },
+ { str: 'if (foo) bar()', source: 'b.js', line: 2, column: 0 },
+ { str: ';\n', source: null, line: null, column: null },
+ { str: '}());', source: null, line: null, column: null },
+ ];
+ var i = 0;
+ node.walk(function (chunk, loc) {
+ assert.equal(expected[i].str, chunk);
+ assert.equal(expected[i].source, loc.source);
+ assert.equal(expected[i].line, loc.line);
+ assert.equal(expected[i].column, loc.column);
+ i++;
+ });
+ };
+
+ exports['test .replaceRight'] = function (assert, util) {
+ var node;
+
+ // Not nested
+ node = new SourceNode(null, null, null, 'hello world');
+ node.replaceRight(/world/, 'universe');
+ assert.equal(node.toString(), 'hello universe');
+
+ // Nested
+ node = new SourceNode(null, null, null,
+ [new SourceNode(null, null, null, 'hey sexy mama, '),
+ new SourceNode(null, null, null, 'want to kill all humans?')]);
+ node.replaceRight(/kill all humans/, 'watch Futurama');
+ assert.equal(node.toString(), 'hey sexy mama, want to watch Futurama?');
+ };
+
+ exports['test .toStringWithSourceMap()'] = forEachNewline(function (assert, util, nl) {
+ var node = new SourceNode(null, null, null,
+ ['(function () {' + nl,
+ ' ',
+ new SourceNode(1, 0, 'a.js', 'someCall', 'originalCall'),
+ new SourceNode(1, 8, 'a.js', '()'),
+ ';' + nl,
+ ' ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';' + nl,
+ '}());']);
+ var result = node.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ assert.equal(result.code, [
+ '(function () {',
+ ' someCall();',
+ ' if (foo) bar();',
+ '}());'
+ ].join(nl));
+
+ var map = result.map;
+ var mapWithoutOptions = node.toStringWithSourceMap().map;
+
+ assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+ assert.ok(mapWithoutOptions instanceof SourceMapGenerator, 'mapWithoutOptions instanceof SourceMapGenerator');
+ assert.ok(!('file' in mapWithoutOptions));
+ mapWithoutOptions._file = 'foo.js';
+ util.assertEqualMaps(assert, map.toJSON(), mapWithoutOptions.toJSON());
+
+ map = new SourceMapConsumer(map.toString());
+
+ var actual;
+
+ actual = map.originalPositionFor({
+ line: 1,
+ column: 4
+ });
+ assert.equal(actual.source, null);
+ assert.equal(actual.line, null);
+ assert.equal(actual.column, null);
+
+ actual = map.originalPositionFor({
+ line: 2,
+ column: 2
+ });
+ assert.equal(actual.source, 'a.js');
+ assert.equal(actual.line, 1);
+ assert.equal(actual.column, 0);
+ assert.equal(actual.name, 'originalCall');
+
+ actual = map.originalPositionFor({
+ line: 3,
+ column: 2
+ });
+ assert.equal(actual.source, 'b.js');
+ assert.equal(actual.line, 2);
+ assert.equal(actual.column, 0);
+
+ actual = map.originalPositionFor({
+ line: 3,
+ column: 16
+ });
+ assert.equal(actual.source, null);
+ assert.equal(actual.line, null);
+ assert.equal(actual.column, null);
+
+ actual = map.originalPositionFor({
+ line: 4,
+ column: 2
+ });
+ assert.equal(actual.source, null);
+ assert.equal(actual.line, null);
+ assert.equal(actual.column, null);
+ });
+
+ exports['test .fromStringWithSourceMap()'] = forEachNewline(function (assert, util, nl) {
+ var testCode = util.testGeneratedCode.replace(/\n/g, nl);
+ var node = SourceNode.fromStringWithSourceMap(
+ testCode,
+ new SourceMapConsumer(util.testMap));
+
+ var result = node.toStringWithSourceMap({
+ file: 'min.js'
+ });
+ var map = result.map;
+ var code = result.code;
+
+ assert.equal(code, testCode);
+ assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+ map = map.toJSON();
+ assert.equal(map.version, util.testMap.version);
+ assert.equal(map.file, util.testMap.file);
+ assert.equal(map.mappings, util.testMap.mappings);
+ });
+
+ exports['test .fromStringWithSourceMap() empty map'] = forEachNewline(function (assert, util, nl) {
+ var node = SourceNode.fromStringWithSourceMap(
+ util.testGeneratedCode.replace(/\n/g, nl),
+ new SourceMapConsumer(util.emptyMap));
+ var result = node.toStringWithSourceMap({
+ file: 'min.js'
+ });
+ var map = result.map;
+ var code = result.code;
+
+ assert.equal(code, util.testGeneratedCode.replace(/\n/g, nl));
+ assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+ map = map.toJSON();
+ assert.equal(map.version, util.emptyMap.version);
+ assert.equal(map.file, util.emptyMap.file);
+ assert.equal(map.mappings.length, util.emptyMap.mappings.length);
+ assert.equal(map.mappings, util.emptyMap.mappings);
+ });
+
+ exports['test .fromStringWithSourceMap() complex version'] = forEachNewline(function (assert, util, nl) {
+ var input = new SourceNode(null, null, null, [
+ "(function() {" + nl,
+ " var Test = {};" + nl,
+ " ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };" + nl),
+ " ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), nl,
+ "}());" + nl,
+ "/* Generated Source */"]);
+ input = input.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ var node = SourceNode.fromStringWithSourceMap(
+ input.code,
+ new SourceMapConsumer(input.map.toString()));
+
+ var result = node.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+ var map = result.map;
+ var code = result.code;
+
+ assert.equal(code, input.code);
+ assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+ map = map.toJSON();
+ var inputMap = input.map.toJSON();
+ util.assertEqualMaps(assert, map, inputMap);
+ });
+
+ exports['test .fromStringWithSourceMap() third argument'] = function (assert, util) {
+ // Assume the following directory structure:
+ //
+ // http://foo.org/
+ // bar.coffee
+ // app/
+ // coffee/
+ // foo.coffee
+ // coffeeBundle.js # Made from {foo,bar,baz}.coffee
+ // maps/
+ // coffeeBundle.js.map
+ // js/
+ // foo.js
+ // public/
+ // app.js # Made from {foo,coffeeBundle}.js
+ // app.js.map
+ //
+ // http://www.example.com/
+ // baz.coffee
+
+ var coffeeBundle = new SourceNode(1, 0, 'foo.coffee', 'foo(coffee);\n');
+ coffeeBundle.setSourceContent('foo.coffee', 'foo coffee');
+ coffeeBundle.add(new SourceNode(2, 0, '/bar.coffee', 'bar(coffee);\n'));
+ coffeeBundle.add(new SourceNode(3, 0, 'http://www.example.com/baz.coffee', 'baz(coffee);'));
+ coffeeBundle = coffeeBundle.toStringWithSourceMap({
+ file: 'foo.js',
+ sourceRoot: '..'
+ });
+
+ var foo = new SourceNode(1, 0, 'foo.js', 'foo(js);');
+
+ var test = function(relativePath, expectedSources) {
+ var app = new SourceNode();
+ app.add(SourceNode.fromStringWithSourceMap(
+ coffeeBundle.code,
+ new SourceMapConsumer(coffeeBundle.map.toString()),
+ relativePath));
+ app.add(foo);
+ var i = 0;
+ app.walk(function (chunk, loc) {
+ assert.equal(loc.source, expectedSources[i]);
+ i++;
+ });
+ app.walkSourceContents(function (sourceFile, sourceContent) {
+ assert.equal(sourceFile, expectedSources[0]);
+ assert.equal(sourceContent, 'foo coffee');
+ })
+ };
+
+ test('../coffee/maps', [
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ // If the third parameter is omitted or set to the current working
+ // directory we get incorrect source paths:
+
+ test(undefined, [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ test('', [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ test('.', [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ test('./', [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+ };
+
+ exports['test .toStringWithSourceMap() merging duplicate mappings'] = forEachNewline(function (assert, util, nl) {
+ var input = new SourceNode(null, null, null, [
+ new SourceNode(1, 0, "a.js", "(function"),
+ new SourceNode(1, 0, "a.js", "() {" + nl),
+ " ",
+ new SourceNode(1, 0, "a.js", "var Test = "),
+ new SourceNode(1, 0, "b.js", "{};" + nl),
+ new SourceNode(2, 0, "b.js", "Test"),
+ new SourceNode(2, 0, "b.js", ".A", "A"),
+ new SourceNode(2, 20, "b.js", " = { value: ", "A"),
+ "1234",
+ new SourceNode(2, 40, "b.js", " };" + nl, "A"),
+ "}());" + nl,
+ "/* Generated Source */"
+ ]);
+ input = input.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ assert.equal(input.code, [
+ "(function() {",
+ " var Test = {};",
+ "Test.A = { value: 1234 };",
+ "}());",
+ "/* Generated Source */"
+ ].join(nl))
+
+ var correctMap = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ correctMap.addMapping({
+ generated: { line: 1, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ // Here is no need for a empty mapping,
+ // because mappings ends at eol
+ correctMap.addMapping({
+ generated: { line: 2, column: 2 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 2, column: 13 },
+ source: 'b.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 4 },
+ source: 'b.js',
+ name: 'A',
+ original: { line: 2, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 6 },
+ source: 'b.js',
+ name: 'A',
+ original: { line: 2, column: 20 }
+ });
+ // This empty mapping is required,
+ // because there is a hole in the middle of the line
+ correctMap.addMapping({
+ generated: { line: 3, column: 18 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 22 },
+ source: 'b.js',
+ name: 'A',
+ original: { line: 2, column: 40 }
+ });
+ // Here is no need for a empty mapping,
+ // because mappings ends at eol
+
+ var inputMap = input.map.toJSON();
+ correctMap = correctMap.toJSON();
+ util.assertEqualMaps(assert, inputMap, correctMap);
+ });
+
+ exports['test .toStringWithSourceMap() multi-line SourceNodes'] = forEachNewline(function (assert, util, nl) {
+ var input = new SourceNode(null, null, null, [
+ new SourceNode(1, 0, "a.js", "(function() {" + nl + "var nextLine = 1;" + nl + "anotherLine();" + nl),
+ new SourceNode(2, 2, "b.js", "Test.call(this, 123);" + nl),
+ new SourceNode(2, 2, "b.js", "this['stuff'] = 'v';" + nl),
+ new SourceNode(2, 2, "b.js", "anotherLine();" + nl),
+ "/*" + nl + "Generated" + nl + "Source" + nl + "*/" + nl,
+ new SourceNode(3, 4, "c.js", "anotherLine();" + nl),
+ "/*" + nl + "Generated" + nl + "Source" + nl + "*/"
+ ]);
+ input = input.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ assert.equal(input.code, [
+ "(function() {",
+ "var nextLine = 1;",
+ "anotherLine();",
+ "Test.call(this, 123);",
+ "this['stuff'] = 'v';",
+ "anotherLine();",
+ "/*",
+ "Generated",
+ "Source",
+ "*/",
+ "anotherLine();",
+ "/*",
+ "Generated",
+ "Source",
+ "*/"
+ ].join(nl));
+
+ var correctMap = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ correctMap.addMapping({
+ generated: { line: 1, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 2, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 4, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 2 }
+ });
+ correctMap.addMapping({
+ generated: { line: 5, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 2 }
+ });
+ correctMap.addMapping({
+ generated: { line: 6, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 2 }
+ });
+ correctMap.addMapping({
+ generated: { line: 11, column: 0 },
+ source: 'c.js',
+ original: { line: 3, column: 4 }
+ });
+
+ var inputMap = input.map.toJSON();
+ correctMap = correctMap.toJSON();
+ util.assertEqualMaps(assert, inputMap, correctMap);
+ });
+
+ exports['test .toStringWithSourceMap() with empty string'] = function (assert, util) {
+ var node = new SourceNode(1, 0, 'empty.js', '');
+ var result = node.toStringWithSourceMap();
+ assert.equal(result.code, '');
+ };
+
+ exports['test .toStringWithSourceMap() with consecutive newlines'] = forEachNewline(function (assert, util, nl) {
+ var input = new SourceNode(null, null, null, [
+ "/***/" + nl + nl,
+ new SourceNode(1, 0, "a.js", "'use strict';" + nl),
+ new SourceNode(2, 0, "a.js", "a();"),
+ ]);
+ input = input.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ assert.equal(input.code, [
+ "/***/",
+ "",
+ "'use strict';",
+ "a();",
+ ].join(nl));
+
+ var correctMap = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 4, column: 0 },
+ source: 'a.js',
+ original: { line: 2, column: 0 }
+ });
+
+ var inputMap = input.map.toJSON();
+ correctMap = correctMap.toJSON();
+ util.assertEqualMaps(assert, inputMap, correctMap);
+ });
+
+ exports['test setSourceContent with toStringWithSourceMap'] = function (assert, util) {
+ var aNode = new SourceNode(1, 1, 'a.js', 'a');
+ aNode.setSourceContent('a.js', 'someContent');
+ var node = new SourceNode(null, null, null,
+ ['(function () {\n',
+ ' ', aNode,
+ ' ', new SourceNode(1, 1, 'b.js', 'b'),
+ '}());']);
+ node.setSourceContent('b.js', 'otherContent');
+ var map = node.toStringWithSourceMap({
+ file: 'foo.js'
+ }).map;
+
+ assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+ map = new SourceMapConsumer(map.toString());
+
+ assert.equal(map.sources.length, 2);
+ assert.equal(map.sources[0], 'a.js');
+ assert.equal(map.sources[1], 'b.js');
+ assert.equal(map.sourcesContent.length, 2);
+ assert.equal(map.sourcesContent[0], 'someContent');
+ assert.equal(map.sourcesContent[1], 'otherContent');
+ };
+
+ exports['test walkSourceContents'] = function (assert, util) {
+ var aNode = new SourceNode(1, 1, 'a.js', 'a');
+ aNode.setSourceContent('a.js', 'someContent');
+ var node = new SourceNode(null, null, null,
+ ['(function () {\n',
+ ' ', aNode,
+ ' ', new SourceNode(1, 1, 'b.js', 'b'),
+ '}());']);
+ node.setSourceContent('b.js', 'otherContent');
+ var results = [];
+ node.walkSourceContents(function (sourceFile, sourceContent) {
+ results.push([sourceFile, sourceContent]);
+ });
+ assert.equal(results.length, 2);
+ assert.equal(results[0][0], 'a.js');
+ assert.equal(results[0][1], 'someContent');
+ assert.equal(results[1][0], 'b.js');
+ assert.equal(results[1][1], 'otherContent');
+ };
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var libUtil = require('../../lib/source-map/util');
+
+ exports['test urls'] = function (assert, util) {
+ var assertUrl = function (url) {
+ assert.equal(url, libUtil.urlGenerate(libUtil.urlParse(url)));
+ };
+ assertUrl('http://');
+ assertUrl('http://www.example.com');
+ assertUrl('http://user:pass@www.example.com');
+ assertUrl('http://www.example.com:80');
+ assertUrl('http://www.example.com/');
+ assertUrl('http://www.example.com/foo/bar');
+ assertUrl('http://www.example.com/foo/bar/');
+ assertUrl('http://user:pass@www.example.com:80/foo/bar/');
+
+ assertUrl('//');
+ assertUrl('//www.example.com');
+ assertUrl('file:///www.example.com');
+
+ assert.equal(libUtil.urlParse(''), null);
+ assert.equal(libUtil.urlParse('.'), null);
+ assert.equal(libUtil.urlParse('..'), null);
+ assert.equal(libUtil.urlParse('a'), null);
+ assert.equal(libUtil.urlParse('a/b'), null);
+ assert.equal(libUtil.urlParse('a//b'), null);
+ assert.equal(libUtil.urlParse('/a'), null);
+ assert.equal(libUtil.urlParse('data:foo,bar'), null);
+ };
+
+ exports['test normalize()'] = function (assert, util) {
+ assert.equal(libUtil.normalize('/..'), '/');
+ assert.equal(libUtil.normalize('/../'), '/');
+ assert.equal(libUtil.normalize('/../../../..'), '/');
+ assert.equal(libUtil.normalize('/../../../../a/b/c'), '/a/b/c');
+ assert.equal(libUtil.normalize('/a/b/c/../../../d/../../e'), '/e');
+
+ assert.equal(libUtil.normalize('..'), '..');
+ assert.equal(libUtil.normalize('../'), '../');
+ assert.equal(libUtil.normalize('../../a/'), '../../a/');
+ assert.equal(libUtil.normalize('a/..'), '.');
+ assert.equal(libUtil.normalize('a/../../..'), '../..');
+
+ assert.equal(libUtil.normalize('/.'), '/');
+ assert.equal(libUtil.normalize('/./'), '/');
+ assert.equal(libUtil.normalize('/./././.'), '/');
+ assert.equal(libUtil.normalize('/././././a/b/c'), '/a/b/c');
+ assert.equal(libUtil.normalize('/a/b/c/./././d/././e'), '/a/b/c/d/e');
+
+ assert.equal(libUtil.normalize(''), '.');
+ assert.equal(libUtil.normalize('.'), '.');
+ assert.equal(libUtil.normalize('./'), '.');
+ assert.equal(libUtil.normalize('././a'), 'a');
+ assert.equal(libUtil.normalize('a/./'), 'a/');
+ assert.equal(libUtil.normalize('a/././.'), 'a');
+
+ assert.equal(libUtil.normalize('/a/b//c////d/////'), '/a/b/c/d/');
+ assert.equal(libUtil.normalize('///a/b//c////d/////'), '///a/b/c/d/');
+ assert.equal(libUtil.normalize('a/b//c////d'), 'a/b/c/d');
+
+ assert.equal(libUtil.normalize('.///.././../a/b//./..'), '../../a')
+
+ assert.equal(libUtil.normalize('http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.normalize('http://www.example.com/'), 'http://www.example.com/');
+ assert.equal(libUtil.normalize('http://www.example.com/./..//a/b/c/.././d//'), 'http://www.example.com/a/b/d/');
+ };
+
+ exports['test join()'] = function (assert, util) {
+ assert.equal(libUtil.join('a', 'b'), 'a/b');
+ assert.equal(libUtil.join('a/', 'b'), 'a/b');
+ assert.equal(libUtil.join('a//', 'b'), 'a/b');
+ assert.equal(libUtil.join('a', 'b/'), 'a/b/');
+ assert.equal(libUtil.join('a', 'b//'), 'a/b/');
+ assert.equal(libUtil.join('a/', '/b'), '/b');
+ assert.equal(libUtil.join('a//', '//b'), '//b');
+
+ assert.equal(libUtil.join('a', '..'), '.');
+ assert.equal(libUtil.join('a', '../b'), 'b');
+ assert.equal(libUtil.join('a/b', '../c'), 'a/c');
+
+ assert.equal(libUtil.join('a', '.'), 'a');
+ assert.equal(libUtil.join('a', './b'), 'a/b');
+ assert.equal(libUtil.join('a/b', './c'), 'a/b/c');
+
+ assert.equal(libUtil.join('a', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('a', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('', 'b'), 'b');
+ assert.equal(libUtil.join('.', 'b'), 'b');
+ assert.equal(libUtil.join('', 'b/'), 'b/');
+ assert.equal(libUtil.join('.', 'b/'), 'b/');
+ assert.equal(libUtil.join('', 'b//'), 'b/');
+ assert.equal(libUtil.join('.', 'b//'), 'b/');
+
+ assert.equal(libUtil.join('', '..'), '..');
+ assert.equal(libUtil.join('.', '..'), '..');
+ assert.equal(libUtil.join('', '../b'), '../b');
+ assert.equal(libUtil.join('.', '../b'), '../b');
+
+ assert.equal(libUtil.join('', '.'), '.');
+ assert.equal(libUtil.join('.', '.'), '.');
+ assert.equal(libUtil.join('', './b'), 'b');
+ assert.equal(libUtil.join('.', './b'), 'b');
+
+ assert.equal(libUtil.join('', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('.', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('', 'data:foo,bar'), 'data:foo,bar');
+ assert.equal(libUtil.join('.', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('..', 'b'), '../b');
+ assert.equal(libUtil.join('..', 'b/'), '../b/');
+ assert.equal(libUtil.join('..', 'b//'), '../b/');
+
+ assert.equal(libUtil.join('..', '..'), '../..');
+ assert.equal(libUtil.join('..', '../b'), '../../b');
+
+ assert.equal(libUtil.join('..', '.'), '..');
+ assert.equal(libUtil.join('..', './b'), '../b');
+
+ assert.equal(libUtil.join('..', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('..', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('a', ''), 'a');
+ assert.equal(libUtil.join('a', '.'), 'a');
+ assert.equal(libUtil.join('a/', ''), 'a');
+ assert.equal(libUtil.join('a/', '.'), 'a');
+ assert.equal(libUtil.join('a//', ''), 'a');
+ assert.equal(libUtil.join('a//', '.'), 'a');
+ assert.equal(libUtil.join('/a', ''), '/a');
+ assert.equal(libUtil.join('/a', '.'), '/a');
+ assert.equal(libUtil.join('', ''), '.');
+ assert.equal(libUtil.join('.', ''), '.');
+ assert.equal(libUtil.join('.', ''), '.');
+ assert.equal(libUtil.join('.', '.'), '.');
+ assert.equal(libUtil.join('..', ''), '..');
+ assert.equal(libUtil.join('..', '.'), '..');
+ assert.equal(libUtil.join('http://foo.org/a', ''), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a/', ''), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a/', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a//', ''), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a//', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org', ''), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org', '.'), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org/', ''), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org/', '.'), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org//', ''), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org//', '.'), 'http://foo.org/');
+ assert.equal(libUtil.join('//www.example.com', ''), '//www.example.com/');
+ assert.equal(libUtil.join('//www.example.com', '.'), '//www.example.com/');
+
+
+ assert.equal(libUtil.join('http://foo.org/a', 'b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a/', 'b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a//', 'b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a', 'b/'), 'http://foo.org/a/b/');
+ assert.equal(libUtil.join('http://foo.org/a', 'b//'), 'http://foo.org/a/b/');
+ assert.equal(libUtil.join('http://foo.org/a/', '/b'), 'http://foo.org/b');
+ assert.equal(libUtil.join('http://foo.org/a//', '//b'), 'http://b');
+
+ assert.equal(libUtil.join('http://foo.org/a', '..'), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org/a', '../b'), 'http://foo.org/b');
+ assert.equal(libUtil.join('http://foo.org/a/b', '../c'), 'http://foo.org/a/c');
+
+ assert.equal(libUtil.join('http://foo.org/a', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a', './b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a/b', './c'), 'http://foo.org/a/b/c');
+
+ assert.equal(libUtil.join('http://foo.org/a', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('http://foo.org/a', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('http://foo.org', 'a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/', 'a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org//', 'a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org', '/a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/', '/a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org//', '/a'), 'http://foo.org/a');
+
+
+ assert.equal(libUtil.join('http://', 'www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('file:///', 'www.example.com'), 'file:///www.example.com');
+ assert.equal(libUtil.join('http://', 'ftp://example.com'), 'ftp://example.com');
+
+ assert.equal(libUtil.join('http://www.example.com', '//foo.org/bar'), 'http://foo.org/bar');
+ assert.equal(libUtil.join('//www.example.com', '//foo.org/bar'), '//foo.org/bar');
+ };
+
+ // TODO Issue #128: Define and test this function properly.
+ exports['test relative()'] = function (assert, util) {
+ assert.equal(libUtil.relative('/the/root', '/the/root/one.js'), 'one.js');
+ assert.equal(libUtil.relative('/the/root', '/the/rootone.js'), '/the/rootone.js');
+
+ assert.equal(libUtil.relative('', '/the/root/one.js'), '/the/root/one.js');
+ assert.equal(libUtil.relative('.', '/the/root/one.js'), '/the/root/one.js');
+ assert.equal(libUtil.relative('', 'the/root/one.js'), 'the/root/one.js');
+ assert.equal(libUtil.relative('.', 'the/root/one.js'), 'the/root/one.js');
+
+ assert.equal(libUtil.relative('/', '/the/root/one.js'), 'the/root/one.js');
+ assert.equal(libUtil.relative('/', 'the/root/one.js'), 'the/root/one.js');
+ };
+
+});
--- /dev/null
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('../../lib/source-map/util');
+
+ // This is a test mapping which maps functions from two different files
+ // (one.js and two.js) to a minified generated source.
+ //
+ // Here is one.js:
+ //
+ // ONE.foo = function (bar) {
+ // return baz(bar);
+ // };
+ //
+ // Here is two.js:
+ //
+ // TWO.inc = function (n) {
+ // return n + 1;
+ // };
+ //
+ // And here is the generated code (min.js):
+ //
+ // ONE.foo=function(a){return baz(a);};
+ // TWO.inc=function(a){return a+1;};
+ exports.testGeneratedCode = " ONE.foo=function(a){return baz(a);};\n"+
+ " TWO.inc=function(a){return a+1;};";
+ exports.testMap = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ sourceRoot: '/the/root',
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+ exports.testMapNoSourceRoot = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+ exports.testMapEmptySourceRoot = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ sourceRoot: '',
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+ // This mapping is identical to above, but uses the indexed format instead.
+ exports.indexedTestMap = {
+ version: 3,
+ file: 'min.js',
+ sections: [
+ {
+ offset: {
+ line: 0,
+ column: 0
+ },
+ map: {
+ version: 3,
+ sources: [
+ "one.js"
+ ],
+ sourcesContent: [
+ ' ONE.foo = function (bar) {\n' +
+ ' return baz(bar);\n' +
+ ' };',
+ ],
+ names: [
+ "bar",
+ "baz"
+ ],
+ mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID",
+ file: "min.js",
+ sourceRoot: "/the/root"
+ }
+ },
+ {
+ offset: {
+ line: 1,
+ column: 0
+ },
+ map: {
+ version: 3,
+ sources: [
+ "two.js"
+ ],
+ sourcesContent: [
+ ' TWO.inc = function (n) {\n' +
+ ' return n + 1;\n' +
+ ' };'
+ ],
+ names: [
+ "n"
+ ],
+ mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
+ file: "min.js",
+ sourceRoot: "/the/root"
+ }
+ }
+ ]
+ };
+ exports.indexedTestMapDifferentSourceRoots = {
+ version: 3,
+ file: 'min.js',
+ sections: [
+ {
+ offset: {
+ line: 0,
+ column: 0
+ },
+ map: {
+ version: 3,
+ sources: [
+ "one.js"
+ ],
+ sourcesContent: [
+ ' ONE.foo = function (bar) {\n' +
+ ' return baz(bar);\n' +
+ ' };',
+ ],
+ names: [
+ "bar",
+ "baz"
+ ],
+ mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID",
+ file: "min.js",
+ sourceRoot: "/the/root"
+ }
+ },
+ {
+ offset: {
+ line: 1,
+ column: 0
+ },
+ map: {
+ version: 3,
+ sources: [
+ "two.js"
+ ],
+ sourcesContent: [
+ ' TWO.inc = function (n) {\n' +
+ ' return n + 1;\n' +
+ ' };'
+ ],
+ names: [
+ "n"
+ ],
+ mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
+ file: "min.js",
+ sourceRoot: "/different/root"
+ }
+ }
+ ]
+ };
+ exports.testMapWithSourcesContent = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ sourcesContent: [
+ ' ONE.foo = function (bar) {\n' +
+ ' return baz(bar);\n' +
+ ' };',
+ ' TWO.inc = function (n) {\n' +
+ ' return n + 1;\n' +
+ ' };'
+ ],
+ sourceRoot: '/the/root',
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+ exports.testMapRelativeSources = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['./one.js', './two.js'],
+ sourcesContent: [
+ ' ONE.foo = function (bar) {\n' +
+ ' return baz(bar);\n' +
+ ' };',
+ ' TWO.inc = function (n) {\n' +
+ ' return n + 1;\n' +
+ ' };'
+ ],
+ sourceRoot: '/the/root',
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+ exports.emptyMap = {
+ version: 3,
+ file: 'min.js',
+ names: [],
+ sources: [],
+ mappings: ''
+ };
+
+
+ function assertMapping(generatedLine, generatedColumn, originalSource,
+ originalLine, originalColumn, name, map, assert,
+ dontTestGenerated, dontTestOriginal) {
+ if (!dontTestOriginal) {
+ var origMapping = map.originalPositionFor({
+ line: generatedLine,
+ column: generatedColumn
+ });
+ assert.equal(origMapping.name, name,
+ 'Incorrect name, expected ' + JSON.stringify(name)
+ + ', got ' + JSON.stringify(origMapping.name));
+ assert.equal(origMapping.line, originalLine,
+ 'Incorrect line, expected ' + JSON.stringify(originalLine)
+ + ', got ' + JSON.stringify(origMapping.line));
+ assert.equal(origMapping.column, originalColumn,
+ 'Incorrect column, expected ' + JSON.stringify(originalColumn)
+ + ', got ' + JSON.stringify(origMapping.column));
+
+ var expectedSource;
+
+ if (originalSource && map.sourceRoot && originalSource.indexOf(map.sourceRoot) === 0) {
+ expectedSource = originalSource;
+ } else if (originalSource) {
+ expectedSource = map.sourceRoot
+ ? util.join(map.sourceRoot, originalSource)
+ : originalSource;
+ } else {
+ expectedSource = null;
+ }
+
+ assert.equal(origMapping.source, expectedSource,
+ 'Incorrect source, expected ' + JSON.stringify(expectedSource)
+ + ', got ' + JSON.stringify(origMapping.source));
+ }
+
+ if (!dontTestGenerated) {
+ var genMapping = map.generatedPositionFor({
+ source: originalSource,
+ line: originalLine,
+ column: originalColumn
+ });
+ assert.equal(genMapping.line, generatedLine,
+ 'Incorrect line, expected ' + JSON.stringify(generatedLine)
+ + ', got ' + JSON.stringify(genMapping.line));
+ assert.equal(genMapping.column, generatedColumn,
+ 'Incorrect column, expected ' + JSON.stringify(generatedColumn)
+ + ', got ' + JSON.stringify(genMapping.column));
+ }
+ }
+ exports.assertMapping = assertMapping;
+
+ function assertEqualMaps(assert, actualMap, expectedMap) {
+ assert.equal(actualMap.version, expectedMap.version, "version mismatch");
+ assert.equal(actualMap.file, expectedMap.file, "file mismatch");
+ assert.equal(actualMap.names.length,
+ expectedMap.names.length,
+ "names length mismatch: " +
+ actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
+ for (var i = 0; i < actualMap.names.length; i++) {
+ assert.equal(actualMap.names[i],
+ expectedMap.names[i],
+ "names[" + i + "] mismatch: " +
+ actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
+ }
+ assert.equal(actualMap.sources.length,
+ expectedMap.sources.length,
+ "sources length mismatch: " +
+ actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
+ for (var i = 0; i < actualMap.sources.length; i++) {
+ assert.equal(actualMap.sources[i],
+ expectedMap.sources[i],
+ "sources[" + i + "] length mismatch: " +
+ actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
+ }
+ assert.equal(actualMap.sourceRoot,
+ expectedMap.sourceRoot,
+ "sourceRoot mismatch: " +
+ actualMap.sourceRoot + " != " + expectedMap.sourceRoot);
+ assert.equal(actualMap.mappings, expectedMap.mappings,
+ "mappings mismatch:\nActual: " + actualMap.mappings + "\nExpected: " + expectedMap.mappings);
+ if (actualMap.sourcesContent) {
+ assert.equal(actualMap.sourcesContent.length,
+ expectedMap.sourcesContent.length,
+ "sourcesContent length mismatch");
+ for (var i = 0; i < actualMap.sourcesContent.length; i++) {
+ assert.equal(actualMap.sourcesContent[i],
+ expectedMap.sourcesContent[i],
+ "sourcesContent[" + i + "] mismatch");
+ }
+ }
+ }
+ exports.assertEqualMaps = assertEqualMaps;
+
+});
--- /dev/null
+{
+ "name": "less",
+ "version": "2.4.0",
+ "description": "Leaner CSS",
+ "homepage": "http://lesscss.org",
+ "author": {
+ "name": "Alexis Sellier",
+ "email": "self@cloudhead.net"
+ },
+ "contributors": [
+ {
+ "name": "The Core Less Team"
+ }
+ ],
+ "bugs": {
+ "url": "https://github.com/less/less.js/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/less/less.js.git"
+ },
+ "licenses": [
+ {
+ "type": "Apache v2",
+ "url": "https://github.com/less/less.js/blob/master/LICENSE"
+ }
+ ],
+ "bin": {
+ "lessc": "./bin/lessc"
+ },
+ "main": "index",
+ "directories": {
+ "test": "./test"
+ },
+ "jam": {
+ "main": "./dist/less.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "grunt test"
+ },
+ "optionalDependencies": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^3.0.5",
+ "image-size": "~0.3.5",
+ "mime": "^1.2.11",
+ "mkdirp": "^0.5.0",
+ "promise": "^6.0.1",
+ "request": "^2.51.0",
+ "source-map": "^0.2.0"
+ },
+ "devDependencies": {
+ "diff": "^1.0",
+ "grunt": "^0.4.5",
+ "grunt-contrib-clean": "^0.6.0",
+ "grunt-contrib-concat": "^0.5.0",
+ "grunt-contrib-connect": "^0.9.0",
+ "grunt-contrib-jasmine": "^0.8.1",
+ "grunt-contrib-jshint": "^0.11.0",
+ "grunt-contrib-uglify": "^0.7.0",
+ "grunt-jscs": "^1.2.0",
+ "grunt-shell": "^1.1.1",
+ "grunt-browserify": "^3.2.0",
+ "matchdep": "^0.3.0",
+ "time-grunt": "^1.0.0",
+ "grunt-saucelabs": "^8.3.2"
+ },
+ "keywords": [
+ "compile less",
+ "css nesting",
+ "css variable",
+ "css",
+ "gradients css",
+ "gradients css3",
+ "less compiler",
+ "less css",
+ "less mixins",
+ "less",
+ "less.js",
+ "lesscss",
+ "mixins",
+ "nested css",
+ "parser",
+ "preprocessor",
+ "bootstrap css",
+ "bootstrap less",
+ "style",
+ "styles",
+ "stylesheet",
+ "variables in css",
+ "css less"
+ ],
+ "gitHead": "6fd2a5751cc8313481913bcb1623bf6c50089df8",
+ "dependencies": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^3.0.5",
+ "image-size": "~0.3.5",
+ "mime": "^1.2.11",
+ "mkdirp": "^0.5.0",
+ "promise": "^6.0.1",
+ "request": "^2.51.0",
+ "source-map": "^0.2.0"
+ },
+ "_id": "less@2.4.0",
+ "_shasum": "ce51b38f1c05a0cdd47982fac40dd0a39cec2031",
+ "_from": "less@>=2.4.0 <2.5.0",
+ "_npmVersion": "2.5.0",
+ "_nodeVersion": "0.10.25",
+ "_npmUser": {
+ "name": "agatronic",
+ "email": "luke.a.page@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "cloudhead",
+ "email": "self@cloudhead.net"
+ },
+ {
+ "name": "agatronic",
+ "email": "luke.a.page@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "ce51b38f1c05a0cdd47982fac40dd0a39cec2031",
+ "tarball": "http://registry.npmjs.org/less/-/less-2.4.0.tgz"
+ },
+ "_resolved": "https://registry.npmjs.org/less/-/less-2.4.0.tgz"
+}
--- /dev/null
+<component name="libraryTable">\r
+ <library name="sass-stdlib">\r
+ <CLASSES />\r
+ <SOURCES>\r
+ <root url="file://$APPLICATION_HOME_DIR$/plugins/sass/lib/stubs/sass_functions.scss" />\r
+ </SOURCES>\r
+ </library>\r
+</component>
\ No newline at end of file
--- /dev/null
+/* Add js reporter for sauce */
+
+jasmine.getEnv().addReporter(new jasmine.JSReporter2());
+
+/* record log messages for testing */
+
+var logMessages = [];
+window.less = window.less || {};
+less.loggers = [
+ {
+ info: function (msg) {
+ logMessages.push(msg);
+ },
+ debug: function (msg) {
+ logMessages.push(msg);
+ },
+ warn: function (msg) {
+ logMessages.push(msg);
+ },
+ error: function (msg) {
+ logMessages.push(msg);
+ }
+ }
+];
+
+var testLessEqualsInDocument = function () {
+ testLessInDocument(testSheet);
+};
+
+var testLessErrorsInDocument = function (isConsole) {
+ testLessInDocument(isConsole ? testErrorSheetConsole : testErrorSheet);
+};
+
+var testLessInDocument = function (testFunc) {
+ var links = document.getElementsByTagName('link'),
+ typePattern = /^text\/(x-)?less$/;
+
+ for (var i = 0; i < links.length; i++) {
+ if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
+ (links[i].type.match(typePattern)))) {
+ testFunc(links[i]);
+ }
+ }
+};
+
+var ieFormat = function(text) {
+ var styleNode = document.createElement('style');
+ styleNode.setAttribute('type', 'text/css');
+ var headNode = document.getElementsByTagName('head')[0];
+ headNode.appendChild(styleNode);
+ try {
+ if (styleNode.styleSheet) {
+ styleNode.styleSheet.cssText = text;
+ } else {
+ styleNode.innerText = text;
+ }
+ } catch (e) {
+ throw new Error("Couldn't reassign styleSheet.cssText.");
+ }
+ var transformedText = styleNode.styleSheet ? styleNode.styleSheet.cssText : styleNode.innerText;
+ headNode.removeChild(styleNode);
+ return transformedText;
+};
+
+var testSheet = function (sheet) {
+ it(sheet.id + " should match the expected output", function (done) {
+ var lessOutputId = sheet.id.replace("original-", ""),
+ expectedOutputId = "expected-" + lessOutputId,
+ lessOutputObj,
+ lessOutput,
+ expectedOutputHref = document.getElementById(expectedOutputId).href,
+ expectedOutput = loadFile(expectedOutputHref);
+
+ // Browser spec generates less on the fly, so we need to loose control
+ less.pageLoadFinished
+ .then(function () {
+ lessOutputObj = document.getElementById(lessOutputId);
+ lessOutput = lessOutputObj.styleSheet ? lessOutputObj.styleSheet.cssText :
+ (lessOutputObj.innerText || lessOutputObj.innerHTML);
+
+ expectedOutput
+ .then(function (text) {
+ if (window.navigator.userAgent.indexOf("MSIE") >= 0 ||
+ window.navigator.userAgent.indexOf("Trident/") >= 0) {
+ text = ieFormat(text);
+ }
+ expect(lessOutput).toEqual(text);
+ done();
+ });
+ });
+ });
+};
+
+//TODO: do it cleaner - the same way as in css
+
+function extractId(href) {
+ return href.replace(/^[a-z-]+:\/+?[^\/]+/i, '') // Remove protocol & domain
+ .replace(/^\//, '') // Remove root /
+ .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension
+ .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
+ .replace(/\./g, ':'); // Replace dots with colons(for valid id)
+}
+
+var waitFor = function (waitFunc) {
+ return new Promise(function (resolve) {
+ var timeoutId = setInterval(function () {
+ if (waitFunc()) {
+ clearInterval(timeoutId);
+ resolve();
+ }
+ }, 5);
+ });
+};
+
+var testErrorSheet = function (sheet) {
+ it(sheet.id + " should match an error", function (done) {
+ var lessHref = sheet.href,
+ id = "less-error-message:" + extractId(lessHref),
+ errorHref = lessHref.replace(/.less$/, ".txt"),
+ errorFile = loadFile(errorHref),
+ actualErrorElement,
+ actualErrorMsg;
+
+ // Less.js sets 10ms timer in order to add error message on top of page.
+ waitFor(function () {
+ actualErrorElement = document.getElementById(id);
+ return actualErrorElement !== null;
+ }).then(function () {
+ var innerText = (actualErrorElement.innerHTML
+ .replace(/<h3>|<\/?p>|<a href="[^"]*">|<\/a>|<ul>|<\/?pre( class="?[^">]*"?)?>|<\/li>|<\/?label>/ig, "")
+ .replace(/<\/h3>/ig, " ")
+ .replace(/<li>|<\/ul>|<br>/ig, "\n"))
+ .replace(/&/ig, "&")
+ // for IE8
+ .replace(/\r\n/g, "\n")
+ .replace(/\. \nin/, ". in");
+ actualErrorMsg = innerText
+ .replace(/\n\d+/g, function (lineNo) {
+ return lineNo + " ";
+ })
+ .replace(/\n\s*in /g, " in ")
+ .replace(/\n{2,}/g, "\n")
+ .replace(/\nStack Trace\n[\s\S]*/i, "")
+ .replace(/\n$/, "");
+ errorFile
+ .then(function (errorTxt) {
+ errorTxt = errorTxt
+ .replace(/\{path\}/g, "")
+ .replace(/\{pathrel\}/g, "")
+ .replace(/\{pathhref\}/g, "http://localhost:8081/test/less/errors/")
+ .replace(/\{404status\}/g, " (404)")
+ .replace(/\{node\}.*\{\/node\}/g, "")
+ .replace(/\n$/, "");
+ expect(actualErrorMsg).toEqual(errorTxt);
+ if (errorTxt == actualErrorMsg) {
+ actualErrorElement.style.display = "none";
+ }
+ done();
+ });
+ });
+ });
+};
+
+var testErrorSheetConsole = function (sheet) {
+ it(sheet.id + " should match an error", function (done) {
+ var lessHref = sheet.href,
+ id = sheet.id.replace(/^original-less:/, "less-error-message:"),
+ errorHref = lessHref.replace(/.less$/, ".txt"),
+ errorFile = loadFile(errorHref),
+ actualErrorElement = document.getElementById(id),
+ actualErrorMsg = logMessages[logMessages.length - 1]
+ .replace(/\nStack Trace\n[\s\S]*/, "");
+
+ describe("the error", function () {
+ expect(actualErrorElement).toBe(null);
+ });
+
+ errorFile
+ .then(function (errorTxt) {
+ errorTxt
+ .replace(/\{path\}/g, "")
+ .replace(/\{pathrel\}/g, "")
+ .replace(/\{pathhref\}/g, "http://localhost:8081/browser/less/")
+ .replace(/\{404status\}/g, " (404)")
+ .replace(/\{node\}.*\{\/node\}/g, "")
+ .trim();
+ expect(actualErrorMsg).toEqual(errorTxt);
+ done();
+ });
+ });
+};
+
+var loadFile = function (href) {
+ return new Promise(function (resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.open('GET', href, true);
+ request.onreadystatechange = function () {
+ if (request.readyState == 4) {
+ resolve(request.responseText.replace(/\r/g, ""));
+ }
+ };
+ request.send(null);
+ });
+};
+
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
--- /dev/null
+.test {
+ color: red;
+}
--- /dev/null
+.testisimported {
+ color: gainsboro;
+}
+.test {
+ color1: green;
+ color2: purple;
+ scalar: 20;
+}
--- /dev/null
+hr {height:50px;}
+.test {
+ color: white;
+}
--- /dev/null
+@import "http://localhost:8081/test/browser/less/imports/modify-this.css";
+@import "http://localhost:8081/test/browser/less/imports/modify-again.css";
+.modify {
+ my-url: url("http://localhost:8081/test/browser/less/imports/a.png");
+}
+.modify {
+ my-url: url("http://localhost:8081/test/browser/less/imports/b.png");
+}
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium), url(http://localhost:8081/test/browser/less/relative-urls/fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(http://localhost:8081/test/browser/less/relative-urls/images/image.jpg);
+ background: url("#inline-svg");
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(http://localhost:8081/test/browser/less/relative-urls/bg.jpg) no-repeat, url(http://localhost:8081/test/browser/less/relative-urls/bg.png) repeat-x top left, url(http://localhost:8081/test/browser/less/relative-urls/bg);
+}
+.values {
+ url: url('http://localhost:8081/test/browser/less/relative-urls/Trebuchet');
+}
--- /dev/null
+@import "https://www.github.com/cloudhead/imports/modify-this.css";
+@import "https://www.github.com/cloudhead/imports/modify-again.css";
+.modify {
+ my-url: url("https://www.github.com/cloudhead/imports/a.png");
+}
+.modify {
+ my-url: url("https://www.github.com/cloudhead/imports/b.png");
+}
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium), url(https://www.github.com/cloudhead/less.js/fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(https://www.github.com/cloudhead/less.js/images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(https://www.github.com/cloudhead/less.js/bg.jpg) no-repeat, url(https://www.github.com/cloudhead/less.js/bg.png) repeat-x top left, url(https://www.github.com/cloudhead/less.js/bg);
+}
+.values {
+ url: url('https://www.github.com/cloudhead/less.js/Trebuchet');
+}
--- /dev/null
+@import "https://localhost/modify-this.css";
+@import "https://localhost/modify-again.css";
+.modify {
+ my-url: url("https://localhost/a.png");
+}
+.modify {
+ my-url: url("https://localhost/b.png");
+}
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium), url(https://localhost/fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(https://localhost/images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(https://localhost/bg.jpg) no-repeat, url(https://localhost/bg.png) repeat-x top left, url(https://localhost/bg);
+}
+.values {
+ url: url('https://localhost/Trebuchet');
+}
--- /dev/null
+@import "http://localhost:8081/test/browser/less/modify-this.css";
+@import "http://localhost:8081/test/browser/less/modify-again.css";
+.modify {
+ my-url: url("http://localhost:8081/test/browser/less/a.png");
+}
+.modify {
+ my-url: url("http://localhost:8081/test/browser/less/b.png");
+}
+.gray-gradient {
+ background: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%2260%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.05%22%2F%3E%3Cstop%20offset%3D%2270%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.1%22%2F%3E%3Cstop%20offset%3D%2273%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.15%22%2F%3E%3Cstop%20offset%3D%2275%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.2%22%2F%3E%3Cstop%20offset%3D%2280%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.25%22%2F%3E%3Cstop%20offset%3D%2285%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.3%22%2F%3E%3Cstop%20offset%3D%2288%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.35%22%2F%3E%3Cstop%20offset%3D%2290%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.4%22%2F%3E%3Cstop%20offset%3D%2295%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.45%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.5%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+}
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium), url(http://localhost:8081/test/browser/less/fonts.svg#MyGeometricModern) format("svg");
+ not-a-comment: url(//z);
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(http://localhost:8081/test/browser/less/images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(http://localhost:8081/test/browser/less/bg.jpg) no-repeat, url(http://localhost:8081/test/browser/less/bg.png) repeat-x top left, url(http://localhost:8081/test/browser/less/bg);
+}
+.values {
+ url: url('http://localhost:8081/test/browser/less/Trebuchet');
+}
+#data-uri {
+ uri: url('http://localhost:8081/test/data/image.jpg');
+}
+#data-uri-guess {
+ uri: url('http://localhost:8081/test/data/image.jpg');
+}
+#data-uri-ascii {
+ uri-1: url('http://localhost:8081/test/data/page.html');
+ uri-2: url('http://localhost:8081/test/data/page.html');
+}
+#data-uri-toobig {
+ uri: url('http://localhost:8081/test/data/data-uri-fail.png');
+}
+#svg-functions {
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23000000%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23000000%22%2F%3E%3Cstop%20offset%3D%223%25%22%20stop-color%3D%22%23ffa500%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%221%25%22%20stop-color%3D%22%23c4c4c4%22%2F%3E%3Cstop%20offset%3D%223%25%22%20stop-color%3D%22%23ffa500%22%2F%3E%3Cstop%20offset%3D%225%25%22%20stop-color%3D%22%23008000%22%2F%3E%3Cstop%20offset%3D%2295%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+}
--- /dev/null
+/*
+ This file is part of the Jasmine JSReporter project from Ivan De Marino.
+
+ Copyright (C) 2011-2014 Ivan De Marino <http://ivandemarino.me>
+ Copyright (C) 2014 Alex Treppass <http://alextreppass.co.uk>
+
+ 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 <organization> 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 IVAN DE MARINO 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.
+ */
+(function (jasmine) {
+
+ if (!jasmine) {
+ throw new Error("[Jasmine JSReporter] 'Jasmine' library not found");
+ }
+
+ // ------------------------------------------------------------------------
+ // Jasmine JSReporter for Jasmine 1.x
+ // ------------------------------------------------------------------------
+
+ /**
+ * Calculate elapsed time, in Seconds.
+ * @param startMs Start time in Milliseconds
+ * @param finishMs Finish time in Milliseconds
+ * @return Elapsed time in Seconds */
+ function elapsedSec (startMs, finishMs) {
+ return (finishMs - startMs) / 1000;
+ }
+
+ /**
+ * Round an amount to the given number of Digits.
+ * If no number of digits is given, than '2' is assumed.
+ * @param amount Amount to round
+ * @param numOfDecDigits Number of Digits to round to. Default value is '2'.
+ * @return Rounded amount */
+ function round (amount, numOfDecDigits) {
+ numOfDecDigits = numOfDecDigits || 2;
+ return Math.round(amount * Math.pow(10, numOfDecDigits)) / Math.pow(10, numOfDecDigits);
+ }
+
+ /**
+ * Create a new array which contains only the failed items.
+ * @param items Items which will be filtered
+ * @returns {Array} of failed items */
+ function failures (items) {
+ var fs = [], i, v;
+ for (i = 0; i < items.length; i += 1) {
+ v = items[i];
+ if (!v.passed_) {
+ fs.push(v);
+ }
+ }
+ return fs;
+ }
+
+ /**
+ * Collect information about a Suite, recursively, and return a JSON result.
+ * @param suite The Jasmine Suite to get data from
+ */
+ function getSuiteData (suite) {
+ var suiteData = {
+ description : suite.description,
+ durationSec : 0,
+ specs: [],
+ suites: [],
+ passed: true
+ },
+ specs = suite.specs(),
+ suites = suite.suites(),
+ i, ilen;
+
+ // Loop over all the Suite's Specs
+ for (i = 0, ilen = specs.length; i < ilen; ++i) {
+ suiteData.specs[i] = {
+ description : specs[i].description,
+ durationSec : specs[i].durationSec,
+ passed : specs[i].results().passedCount === specs[i].results().totalCount,
+ skipped : specs[i].results().skipped,
+ passedCount : specs[i].results().passedCount,
+ failedCount : specs[i].results().failedCount,
+ totalCount : specs[i].results().totalCount,
+ failures: failures(specs[i].results().getItems())
+ };
+ suiteData.passed = !suiteData.specs[i].passed ? false : suiteData.passed;
+ suiteData.durationSec += suiteData.specs[i].durationSec;
+ }
+
+ // Loop over all the Suite's sub-Suites
+ for (i = 0, ilen = suites.length; i < ilen; ++i) {
+ suiteData.suites[i] = getSuiteData(suites[i]); //< recursive population
+ suiteData.passed = !suiteData.suites[i].passed ? false : suiteData.passed;
+ suiteData.durationSec += suiteData.suites[i].durationSec;
+ }
+
+ // Rounding duration numbers to 3 decimal digits
+ suiteData.durationSec = round(suiteData.durationSec, 4);
+
+ return suiteData;
+ }
+
+ var JSReporter = function () {
+ };
+
+ JSReporter.prototype = {
+ reportRunnerStarting: function (runner) {
+ // Nothing to do
+ },
+
+ reportSpecStarting: function (spec) {
+ // Start timing this spec
+ spec.startedAt = new Date();
+ },
+
+ reportSpecResults: function (spec) {
+ // Finish timing this spec and calculate duration/delta (in sec)
+ spec.finishedAt = new Date();
+ // If the spec was skipped, reportSpecStarting is never called and spec.startedAt is undefined
+ spec.durationSec = spec.startedAt ? elapsedSec(spec.startedAt.getTime(), spec.finishedAt.getTime()) : 0;
+ },
+
+ reportSuiteResults: function (suite) {
+ // Nothing to do
+ },
+
+ reportRunnerResults: function (runner) {
+ var suites = runner.suites(),
+ i, j, ilen;
+
+ // Attach results to the "jasmine" object to make those results easy to scrap/find
+ jasmine.runnerResults = {
+ suites: [],
+ durationSec : 0,
+ passed : true
+ };
+
+ // Loop over all the Suites
+ for (i = 0, ilen = suites.length, j = 0; i < ilen; ++i) {
+ if (suites[i].parentSuite === null) {
+ jasmine.runnerResults.suites[j] = getSuiteData(suites[i]);
+ // If 1 suite fails, the whole runner fails
+ jasmine.runnerResults.passed = !jasmine.runnerResults.suites[j].passed ? false : jasmine.runnerResults.passed;
+ // Add up all the durations
+ jasmine.runnerResults.durationSec += jasmine.runnerResults.suites[j].durationSec;
+ j++;
+ }
+ }
+
+ // Decorate the 'jasmine' object with getters
+ jasmine.getJSReport = function () {
+ if (jasmine.runnerResults) {
+ return jasmine.runnerResults;
+ }
+ return null;
+ };
+ jasmine.getJSReportAsString = function () {
+ return JSON.stringify(jasmine.getJSReport());
+ };
+ }
+ };
+
+ // export public
+ jasmine.JSReporter = JSReporter;
+
+ // ------------------------------------------------------------------------
+ // Jasmine JSReporter for Jasmine 2.0
+ // ------------------------------------------------------------------------
+
+ /*
+ Simple timer implementation
+ */
+ var Timer = function () {};
+
+ Timer.prototype.start = function () {
+ this.startTime = new Date().getTime();
+ return this;
+ };
+
+ Timer.prototype.elapsed = function () {
+ if (this.startTime == null) {
+ return -1;
+ }
+ return new Date().getTime() - this.startTime;
+ };
+
+ /*
+ Utility methods
+ */
+ var _extend = function (obj1, obj2) {
+ for (var prop in obj2) {
+ obj1[prop] = obj2[prop];
+ }
+ return obj1;
+ };
+ var _clone = function (obj) {
+ if (obj !== Object(obj)) {
+ return obj;
+ }
+ return _extend({}, obj);
+ };
+
+ jasmine.JSReporter2 = function () {
+ this.specs = {};
+ this.suites = {};
+ this.rootSuites = [];
+ this.suiteStack = [];
+
+ // export methods under jasmine namespace
+ jasmine.getJSReport = this.getJSReport;
+ jasmine.getJSReportAsString = this.getJSReportAsString;
+ };
+
+ var JSR = jasmine.JSReporter2.prototype;
+
+ // Reporter API methods
+ // --------------------
+
+ JSR.suiteStarted = function (suite) {
+ suite = this._cacheSuite(suite);
+ // build up suite tree as we go
+ suite.specs = [];
+ suite.suites = [];
+ suite.passed = true;
+ suite.parentId = this.suiteStack.slice(this.suiteStack.length - 1)[0];
+ if (suite.parentId) {
+ this.suites[suite.parentId].suites.push(suite);
+ } else {
+ this.rootSuites.push(suite.id);
+ }
+ this.suiteStack.push(suite.id);
+ suite.timer = new Timer().start();
+ };
+
+ JSR.suiteDone = function (suite) {
+ suite = this._cacheSuite(suite);
+ suite.duration = suite.timer.elapsed();
+ suite.durationSec = suite.duration / 1000;
+ this.suiteStack.pop();
+
+ // maintain parent suite state
+ var parent = this.suites[suite.parentId];
+ if (parent) {
+ parent.passed = parent.passed && suite.passed;
+ }
+
+ // keep report representation clean
+ delete suite.timer;
+ delete suite.id;
+ delete suite.parentId;
+ delete suite.fullName;
+ };
+
+ JSR.specStarted = function (spec) {
+ spec = this._cacheSpec(spec);
+ spec.timer = new Timer().start();
+ // build up suites->spec tree as we go
+ spec.suiteId = this.suiteStack.slice(this.suiteStack.length - 1)[0];
+ this.suites[spec.suiteId].specs.push(spec);
+ };
+
+ JSR.specDone = function (spec) {
+ spec = this._cacheSpec(spec);
+
+ spec.duration = spec.timer.elapsed();
+ spec.durationSec = spec.duration / 1000;
+
+ spec.skipped = spec.status === 'pending';
+ spec.passed = spec.skipped || spec.status === 'passed';
+
+ spec.totalCount = spec.passedExpectations.length + spec.failedExpectations.length;
+ spec.passedCount = spec.passedExpectations.length;
+ spec.failedCount = spec.failedExpectations.length;
+ spec.failures = [];
+
+ for (var i = 0, j = spec.failedExpectations.length; i < j; i++) {
+ var fail = spec.failedExpectations[i];
+ spec.failures.push({
+ type: 'expect',
+ expected: fail.expected,
+ passed: false,
+ message: fail.message,
+ matcherName: fail.matcherName,
+ trace: {
+ stack: fail.stack
+ }
+ });
+ }
+
+ // maintain parent suite state
+ var parent = this.suites[spec.suiteId];
+ if (spec.failed) {
+ parent.failingSpecs.push(spec);
+ }
+ parent.passed = parent.passed && spec.passed;
+
+ // keep report representation clean
+ delete spec.timer;
+ delete spec.totalExpectations;
+ delete spec.passedExpectations;
+ delete spec.suiteId;
+ delete spec.fullName;
+ delete spec.id;
+ delete spec.status;
+ delete spec.failedExpectations;
+ };
+
+ JSR.jasmineDone = function () {
+ this._buildReport();
+ };
+
+ JSR.getJSReport = function () {
+ if (jasmine.jsReport) {
+ return jasmine.jsReport;
+ }
+ };
+
+ JSR.getJSReportAsString = function () {
+ if (jasmine.jsReport) {
+ return JSON.stringify(jasmine.jsReport);
+ }
+ };
+
+ // Private methods
+ // ---------------
+
+ JSR._haveSpec = function (spec) {
+ return this.specs[spec.id] != null;
+ };
+
+ JSR._cacheSpec = function (spec) {
+ var existing = this.specs[spec.id];
+ if (existing == null) {
+ existing = this.specs[spec.id] = _clone(spec);
+ } else {
+ _extend(existing, spec);
+ }
+ return existing;
+ };
+
+ JSR._haveSuite = function (suite) {
+ return this.suites[suite.id] != null;
+ };
+
+ JSR._cacheSuite = function (suite) {
+ var existing = this.suites[suite.id];
+ if (existing == null) {
+ existing = this.suites[suite.id] = _clone(suite);
+ } else {
+ _extend(existing, suite);
+ }
+ return existing;
+ };
+
+ JSR._buildReport = function () {
+ var overallDuration = 0;
+ var overallPassed = true;
+ var overallSuites = [];
+
+ for (var i = 0, j = this.rootSuites.length; i < j; i++) {
+ var suite = this.suites[this.rootSuites[i]];
+ overallDuration += suite.duration;
+ overallPassed = overallPassed && suite.passed;
+ overallSuites.push(suite);
+ }
+
+ jasmine.jsReport = {
+ passed: overallPassed,
+ durationSec: overallDuration / 1000,
+ suites: overallSuites
+ };
+ };
+
+})(jasmine);
--- /dev/null
+.a {
+ prop: (3 / #fff);
+}
\ No newline at end of file
--- /dev/null
+less: OperationError: Can't substract or divide a color from a number in {pathhref}console-errors/test-error.less on line null, column 0:
+1 prop: (3 / #fff);
--- /dev/null
+.test {
+ color: @global-var;
+}
--- /dev/null
+@import "modify-this.css";
+.modify {
+ my-url: url("a.png");
+}
\ No newline at end of file
--- /dev/null
+@import "modify-again.css";
+.modify {
+ my-url: url("b.png");
+}
\ No newline at end of file
--- /dev/null
+@var2: blue;
+.testisimported {
+ color: gainsboro;
+}
\ No newline at end of file
--- /dev/null
+@import "imports/simple2";
+@var1: red;
+@scale: 10;
+.test {
+ color1: @var1;
+ color2: @var2;
+ scalar: @scale
+}
\ No newline at end of file
--- /dev/null
+@import "svg-gradient-mixin.less";
+
+.gray-gradient {
+ .gradient-mixin(#999);
+}
--- /dev/null
+.gradient-mixin(@color) {
+ background: svg-gradient(to bottom,
+ fade(@color, 0%) 0%,
+ fade(@color, 5%) 60%,
+ fade(@color, 10%) 70%,
+ fade(@color, 15%) 73%,
+ fade(@color, 20%) 75%,
+ fade(@color, 25%) 80%,
+ fade(@color, 30%) 85%,
+ fade(@color, 35%) 88%,
+ fade(@color, 40%) 90%,
+ fade(@color, 45%) 95%,
+ fade(@color, 50%) 100%
+ );
+}
--- /dev/null
+@color: white;
+.test {
+ color: @color;
+}
--- /dev/null
+@import ".././imports/urls.less";
+@import "http://localhost:8081/test/browser/less/imports/urls2.less";
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(images/image.jpg);
+ background: url("#inline-svg");
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
--- /dev/null
+@import "../imports/urls.less";
+@import "http://localhost:8081/test/browser/less/imports/urls2.less";
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
--- /dev/null
+@import "../imports/urls.less";
+@import "http://localhost:8081/test/browser/less/imports/urls2.less";
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
--- /dev/null
+@import "imports/urls.less";
+@import "http://localhost:8081/test/browser/less/imports/urls2.less";
+@import "http://localhost:8081/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less";
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+ not-a-comment: url(//z);
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
+#data-uri {
+ uri: data-uri('image/jpeg;base64', '../../data/image.jpg');
+}
+
+#data-uri-guess {
+ uri: data-uri('../../data/image.jpg');
+}
+
+#data-uri-ascii {
+ uri-1: data-uri('text/html', '../../data/page.html');
+ uri-2: data-uri('../../data/page.html');
+}
+
+#data-uri-toobig {
+ uri: data-uri('../../data/data-uri-fail.png');
+}
+#svg-functions {
+ background-image: svg-gradient(to bottom, black, white);
+ background-image: svg-gradient(to bottom, black, orange 3%, white);
+ @green_5: green 5%;
+ @orange_percentage: 3%;
+ @orange_color: orange;
+ background-image: svg-gradient(to bottom, (mix(black, white) + #444) 1%, @orange_color @orange_percentage, ((@green_5)), white 95%);
+}
--- /dev/null
+var less = {logLevel: 4, errorReporting: "console"};
+
+// There originally run inside describe method. However, since they have not
+// been inside it, they run at jasmine compile time (not runtime). It all
+// worked cause less.js was in async mode and custom phantom runner had
+// different setup then grunt-contrib-jasmine. They have been created before
+// less.js run, even as they have been defined in spec.
+
+// test inline less in style tags by grabbing an assortment of less files and doing `@import`s
+var testFiles = ['charsets', 'colors', 'comments', 'css-3', 'strings', 'media', 'mixins'],
+ testSheets = [];
+
+// IE 8-10 does not support less in style tags
+if (window.navigator.userAgent.indexOf("MSIE") >= 0) {
+ testFiles.length = 0;
+}
+
+// setup style tags with less and link tags pointing to expected css output
+
+for (var i = 0; i < testFiles.length; i++) {
+ var file = testFiles[i],
+ lessPath = '/test/less/' + file + '.less',
+ cssPath = '/test/css/' + file + '.css',
+ lessStyle = document.createElement('style'),
+ cssLink = document.createElement('link'),
+ lessText = '@import "' + lessPath + '";';
+
+ lessStyle.type = 'text/less';
+ lessStyle.id = file;
+ lessStyle.href = file;
+
+ if (lessStyle.styleSheet === undefined) {
+ lessStyle.appendChild(document.createTextNode(lessText));
+ }
+
+ cssLink.rel = 'stylesheet';
+ cssLink.type = 'text/css';
+ cssLink.href = cssPath;
+ cssLink.id = 'expected-' + file;
+
+ var head = document.getElementsByTagName('head')[0];
+
+ head.appendChild(lessStyle);
+
+ if (lessStyle.styleSheet) {
+ lessStyle.styleSheet.cssText = lessText;
+ }
+
+ head.appendChild(cssLink);
+ testSheets[i] = lessStyle;
+}
--- /dev/null
+describe("less.js browser behaviour", function() {
+ testLessEqualsInDocument();
+
+ it("has some log messages", function() {
+ expect(logMessages.length).toBeGreaterThan(0);
+ });
+
+ for (var i = 0; i < testFiles.length; i++) {
+ var sheet = testSheets[i];
+ testSheet(sheet);
+ }
+});
--- /dev/null
+less.errorReporting = 'console';
+
+describe("less.js error reporting console test", function() {
+ testLessErrorsInDocument(true);
+});
\ No newline at end of file
--- /dev/null
+var less = {
+ strictUnits: true,
+ strictMath: true,
+ logLevel: 4 };
--- /dev/null
+describe("less.js error tests", function() {
+ testLessErrorsInDocument();
+});
--- /dev/null
+var less = {
+ logLevel: 4,
+ errorReporting: "console",
+ globalVars: {
+ "@global-var": "red"
+}};
--- /dev/null
+describe("less.js global vars", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+var less = {
+ logLevel: 4,
+ errorReporting: "console",
+ strictMath: false,
+ strictUnits: false };
--- /dev/null
+describe("less.js legacy tests", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+var less = {
+ logLevel: 4,
+ errorReporting: "console"
+};
+less.strictMath = true;
+less.functions = {
+ add: function(a, b) {
+ return new(less.tree.Dimension)(a.value + b.value);
+ },
+ increment: function(a) {
+ return new(less.tree.Dimension)(a.value + 1);
+ },
+ _color: function(str) {
+ if (str.value === "evil red") {
+ return new(less.tree.Color)("600");
+ }
+ }
+};
\ No newline at end of file
--- /dev/null
+console.warn("start spec");
+describe("less.js main tests", function() {
+ testLessEqualsInDocument();
+ it("the global environment", function() {
+ expect(window.require).toBe(undefined);
+ });
+});
--- /dev/null
+/* exported less */
+var less = {
+ logLevel: 4,
+ errorReporting: "console"
+};
\ No newline at end of file
--- /dev/null
+var alreadyRun = false;
+
+describe("less.js modify vars", function () {
+ beforeEach(function (done) {
+ // simulating "setUp" or "beforeAll" method
+ if (alreadyRun) {
+ done();
+ return;
+ }
+
+ alreadyRun = true;
+
+ less.pageLoadFinished
+ .then(function () {
+ less.modifyVars({
+ var1: "green",
+ var2: "purple",
+ scale: 20
+ }).then(function () {
+ done();
+ });
+ });
+ });
+
+ testLessEqualsInDocument();
+ it("Should log only 2 XHR requests", function (done) {
+ var xhrLogMessages = logMessages.filter(function (item) {
+ return (/XHR: Getting '/).test(item);
+ });
+ expect(xhrLogMessages.length).toEqual(2);
+ done();
+ });
+});
--- /dev/null
+var less = {logLevel: 4};
+
+less.strictUnits = true;
+less.javascriptEnabled = false;
--- /dev/null
+describe("less.js javascript disabled error tests", function() {
+ testLessErrorsInDocument();
+});
--- /dev/null
+var less = {logLevel: 4,
+ errorReporting: "console"};
+less.postProcessor = function(styles) {
+ return 'hr {height:50px;}\n' + styles;
+};
--- /dev/null
+describe("less.js postProcessor", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+var less = {logLevel: 4,
+ errorReporting: "console"};
+less.env = "production";
--- /dev/null
+describe("less.js production behaviour", function() {
+ it("doesn't log any messages", function() {
+ expect(logMessages.length).toEqual(0);
+ });
+});
--- /dev/null
+var less = {logLevel: 4,
+ errorReporting: "console"};
+less.relativeUrls = true;
--- /dev/null
+describe("less.js browser test - relative url's", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+var less = {logLevel: 4,
+ errorReporting: "console"};
+less.rootpath = "https://localhost/";
--- /dev/null
+var less = {logLevel: 4,
+ errorReporting: "console"};
+less.rootpath = "https://www.github.com/cloudhead/less.js/";
+less.relativeUrls = true;
--- /dev/null
+describe("less.js browser test - rootpath and relative url's", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+describe("less.js browser test - rootpath url's", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+var less = {
+ logLevel: 4,
+ errorReporting: "console",
+ strictMath: true,
+ strictUnits: true };
--- /dev/null
+describe("less.js strict units tests", function() {
+ testLessEqualsInDocument();
+});
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Jasmine Spec Runner</title>
+
+ <!-- generate script tags for tests -->
+ <% var generateScriptTags = function(allScripts) { allScripts.forEach(function(script){ %>
+ <script src="<%= script %>"></script>
+ <% }); }; %>
+
+ <!-- generate script tags for tests -->
+ <% var toArray = function(scripts) {
+ %>[<%
+ scripts.forEach(function(scriptUrl, index){
+ %>"<%= scriptUrl %>"<%
+ if (index !== scripts.length -1) {
+ %>,<%
+ }
+ });
+ %>]<%
+ }; %>
+
+ <!-- for each test, generate CSS/LESS link tags -->
+ <% scripts.src.forEach(function(fullLessName) {
+ var pathParts = fullLessName.split('/');
+ var fullCssName = fullLessName.replace(/less/g, 'css');
+ var lessName = pathParts[pathParts.length - 1];
+ var name = lessName.split('.')[0]; %>
+ <!-- the tags to be generated -->
+ <link id="original-less:test-less-<%= name %>" title="test-less-<%= name %>" rel="stylesheet/less" type="text/css" href="<%= fullLessName %>">
+ <link id="expected-less:test-less-<%= name %>" rel="stylesheet" type="text/css" href="<%= fullCssName %>">
+ <% }); %>
+
+ <!-- generate grunt-contrib-jasmine link tags -->
+ <% css.forEach(function(style){ %>
+ <link rel="stylesheet" type="text/css" href="<%= style %>">
+ <% }) %>
+
+ <script>
+
+ function loadScript(url,callback){
+ var script = document.createElement('script');
+
+ if(document.documentMode === 8){
+ script.onreadystatechange = function(){
+ if (script.readyState === 'loaded'){
+ if (callback){callback()};
+ };
+ };
+ } else {
+ script.onload = function(){
+ if (callback){callback()};
+ };
+ };
+ script.src = url;
+ document.body.appendChild(script);
+ };
+
+
+ // allow sauce to query for the jasmine report
+ // because we have to load the page before starting the test, so the thing
+ // sauce queries for might not be setup yet
+ window.jasmine = { getJSReport: function() { } };
+ setTimeout(function() {
+ var jasmine = <% toArray([].concat(scripts.polyfills, scripts.jasmine, scripts.boot)) %>,
+ helpers = <% toArray(scripts.helpers) %>,
+ vendor = <% toArray(scripts.vendor) %>,
+ specs = <% toArray(scripts.specs) %>,
+ reporters = <% toArray([].concat(scripts.reporters)) %>,
+ allScripts = jasmine.concat(helpers).concat(vendor).concat(specs).concat(reporters);
+
+ function addNextScript() {
+ // for sauce, see above. Additional step needed between loading jasmine and loading
+ // the js reporter
+ if (!jasmine.getJSReport) {
+ jasmine.getJSReport = function() {};
+ }
+ if (allScripts.length) {
+ var scriptSrc = allScripts.shift();
+ loadScript(scriptSrc, addNextScript);
+ } else {
+ window.onload();
+ }
+ }
+ addNextScript();
+
+ },1000);
+ </script>
+ </head>
+
+ <body>
+ <!-- content -->
+ </body>
+</html>
--- /dev/null
+/*jshint latedef: nofunc */
+
+// This is used to copy a folder (the test/less/* files & sub-folders), adding a BOM to the start of each LESS and CSS file.
+// This is a based on the copySync method from fs-extra (https://github.com/jprichardson/node-fs-extra/).
+
+module.exports = function() {
+ var path = require('path'),
+ fs = require('fs');
+
+ var BUF_LENGTH = 64 * 1024;
+ var _buff = new Buffer(BUF_LENGTH);
+
+ function copyFolderWithBom(src, dest) {
+ var stats = fs.lstatSync(src);
+ var destFolder = path.dirname(dest);
+ var destFolderExists = fs.existsSync(destFolder);
+ var performCopy = false;
+
+ if (stats.isFile()) {
+ if (!destFolderExists) {
+ fs.mkdirSync(destFolder);
+ }
+ if (src.match(/\.(css|less)$/)) {
+ copyFileAddingBomSync(src, dest);
+ } else {
+ copyFileSync(src, dest);
+ }
+ }
+ else if (stats.isDirectory()) {
+ if (!fs.existsSync(destFolder)) {
+ fs.mkdirSync(destFolder);
+ }
+ if (!fs.existsSync(dest)) {
+ fs.mkdirSync(dest);
+ }
+ fs.readdirSync(src).forEach(function(d) {
+ if (d !== 'bom') {
+ copyFolderWithBom(path.join(src, d), path.join(dest, d));
+ }
+ });
+ }
+ }
+
+ function copyFileAddingBomSync(srcFile, destFile) {
+ var contents = fs.readFileSync(srcFile, { encoding: 'utf8' });
+ if (!contents.length || contents.charCodeAt(0) !== 0xFEFF) {
+ contents = '\ufeff' + contents;
+ }
+ fs.writeFileSync(destFile, contents, { encoding: 'utf8' });
+ }
+
+ function copyFileSync(srcFile, destFile) {
+ var fdr = fs.openSync(srcFile, 'r');
+ var stat = fs.fstatSync(fdr);
+ var fdw = fs.openSync(destFile, 'w', stat.mode);
+ var bytesRead = 1;
+ var pos = 0;
+
+ while (bytesRead > 0) {
+ bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
+ fs.writeSync(fdw, _buff, 0, bytesRead);
+ pos += bytesRead;
+ }
+
+ fs.closeSync(fdr);
+ fs.closeSync(fdw);
+ }
+
+ return {
+ copyFolderWithBom: copyFolderWithBom
+ };
+};
--- /dev/null
+@charset "UTF-8";
--- /dev/null
+#yelow #short {
+ color: #fea;
+}
+#yelow #long {
+ color: #ffeeaa;
+}
+#yelow #rgba {
+ color: rgba(255, 238, 170, 0.1);
+}
+#yelow #argb {
+ color: #1affeeaa;
+}
+#blue #short {
+ color: #00f;
+}
+#blue #long {
+ color: #0000ff;
+}
+#blue #rgba {
+ color: rgba(0, 0, 255, 0.1);
+}
+#blue #argb {
+ color: #1a0000ff;
+}
+#alpha #hsla {
+ color: rgba(61, 45, 41, 0.6);
+}
+#overflow .a {
+ color: #000000;
+}
+#overflow .b {
+ color: #ffffff;
+}
+#overflow .c {
+ color: #ffffff;
+}
+#overflow .d {
+ color: #00ff00;
+}
+#overflow .e {
+ color: rgba(0, 31, 255, 0.42);
+}
+#grey {
+ color: #c8c8c8;
+}
+#333333 {
+ color: #333333;
+}
+#808080 {
+ color: #808080;
+}
+#00ff00 {
+ color: #00ff00;
+}
+.lightenblue {
+ color: #3333ff;
+}
+.darkenblue {
+ color: #0000cc;
+}
+.unknowncolors {
+ color: blue2;
+ border: 2px solid superred;
+}
+.transparent {
+ color: transparent;
+ background-color: rgba(0, 0, 0, 0);
+}
+#alpha #fromvar {
+ opacity: 0.7;
+}
+#alpha #short {
+ opacity: 1;
+}
+#alpha #long {
+ opacity: 1;
+}
+#alpha #rgba {
+ opacity: 0.2;
+}
+#alpha #hsl {
+ opacity: 1;
+}
+#percentage {
+ color: 255;
+ border-color: rgba(255, 0, 0, 0.5);
+}
--- /dev/null
+/******************\
+* *
+* Comment Header *
+* *
+\******************/
+/*
+
+ Comment
+
+*/
+/*
+ * Comment Test
+ *
+ * - cloudhead (http://cloudhead.net)
+ *
+ */
+/* Colors
+ * ------
+ * #EDF8FC (background blue)
+ * #166C89 (darkest blue)
+ *
+ * Text:
+ * #333 (standard text) // A comment within a comment!
+ * #1F9EC9 (standard link)
+ *
+ */
+/* @group Variables
+------------------- */
+#comments,
+.comments {
+ /**/
+ color: red;
+ /* A C-style comment */
+ /* A C-style comment */
+ background-color: orange;
+ font-size: 12px;
+ /* lost comment */
+ content: "content";
+ border: 1px solid black;
+ padding: 0;
+ margin: 2em;
+}
+/* commented out
+ #more-comments {
+ color: grey;
+ }
+*/
+.selector,
+.lots,
+.comments {
+ color: grey, /* blue */ orange;
+ -webkit-border-radius: 2px /* webkit only */;
+ -moz-border-radius: 8px /* moz only with operation */;
+}
+.test {
+ color: 1px;
+}
+.sr-only-focusable {
+ clip: auto;
+}
+@-webkit-keyframes hover {
+ /* and Chrome */
+ 0% {
+ color: red;
+ }
+}
+#last {
+ color: blue;
+}
+/* */
+/* { */
+/* */
+/* */
+/* */
+#div {
+ color: #A33;
+}
+/* } */
+/*by block */
+#output-block {
+ comment: /* // Not commented out // */;
+}
+/*comment on last line*/
--- /dev/null
+@-webkit-keyframes hover {
+ /* Safari and Chrome */
+}
+.bg {
+ background-image: linear-gradient(#333333 /*{comment}*/, #111111);
+}
+#planadvisor,
+.first,
+.planning {
+ margin: 10px;
+ total-width: (1 * 6em * 12) + (2em * 12);
+}
--- /dev/null
+#colours{color1:#fea;color2:#fea;color3:rgba(255,238,170,0.1);string:"#ffeeaa";/*! but not this type
+ Note preserved whitespace
+ */}dimensions{val:.1px;val:0;val:4cm;val:.2;val:5;angles-must-have-unit:0deg;durations-must-have-unit:0s;length-doesnt-have-unit:0;width:auto\9}@page{marks:none;@top-left-corner{vertical-align:top}@top-left{vertical-align:top}}.shadow^.dom,body^^.shadow{display:done}
\ No newline at end of file
--- /dev/null
+.comma-delimited {
+ text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
+ -moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset, 0pt 4px 6px rgba(255, 255, 255, 0.4) inset;
+ -webkit-transform: rotate(0deg);
+}
+@font-face {
+ font-family: Headline;
+ unicode-range: U+??????, U+0???, U+0-7F, U+A5;
+}
+.other {
+ -moz-transform: translate(0, 11em) rotate(-90deg);
+ transform: rotateX(45deg);
+}
+.item[data-cra_zy-attr1b-ut3=bold] {
+ font-weight: bold;
+}
+p:not([class*="lead"]) {
+ color: black;
+}
+input[type="text"].class#id[attr=32]:not(1) {
+ color: white;
+}
+div#id.class[a=1][b=2].class:not(1) {
+ color: white;
+}
+ul.comma > li:not(:only-child)::after {
+ color: white;
+}
+ol.comma > li:nth-last-child(2)::after {
+ color: white;
+}
+li:nth-child(4n+1),
+li:nth-child(-5n),
+li:nth-child(-n+2) {
+ color: white;
+}
+a[href^="http://"] {
+ color: black;
+}
+a[href$="http://"] {
+ color: black;
+}
+form[data-disabled] {
+ color: black;
+}
+p::before {
+ color: black;
+}
+#issue322 {
+ -webkit-animation: anim2 7s infinite ease-in-out;
+}
+@-webkit-keyframes frames {
+ 0% {
+ border: 1px;
+ }
+ 5.5% {
+ border: 2px;
+ }
+ 100% {
+ border: 3px;
+ }
+}
+@keyframes fontbulger1 {
+ to {
+ font-size: 15px;
+ }
+ from,
+ to {
+ font-size: 12px;
+ }
+ 0%,
+ 100% {
+ font-size: 12px;
+ }
+}
+.units {
+ font: 1.2rem/2rem;
+ font: 8vw/9vw;
+ font: 10vh/12vh;
+ font: 12vm/15vm;
+ font: 12vmin/15vmin;
+ font: 1.2ch/1.5ch;
+}
+@supports ( box-shadow: 2px 2px 2px black ) or
+ ( -moz-box-shadow: 2px 2px 2px black ) {
+ .outline {
+ box-shadow: 2px 2px 2px black;
+ -moz-box-shadow: 2px 2px 2px black;
+ }
+}
+@-x-document url-prefix(""github.com"") {
+ h1 {
+ color: red;
+ }
+}
+@viewport {
+ font-size: 10px;
+}
+@namespace foo url(http://www.example.com);
+foo|h1 {
+ color: blue;
+}
+foo|* {
+ color: yellow;
+}
+|h1 {
+ color: red;
+}
+*|h1 {
+ color: green;
+}
+h1 {
+ color: green;
+}
+.upper-test {
+ UpperCaseProperties: allowed;
+}
+@host {
+ div {
+ display: block;
+ }
+}
+::distributed(input::placeholder) {
+ color: #b3b3b3;
+}
+.shadow ^ .dom,
+body ^^ .shadow {
+ display: done;
+}
+:host(.sel .a),
+:host-context(.sel .b),
+.sel /deep/ .b,
+::content .sel {
+ type: shadow-dom;
+}
+ /deep/ b {
+ c: 'd';
+}
+ /deep/ b[e] {
+ f: 'g';
+}
+#issue2066 {
+ background: url('/images/icon-team.svg') 0 0 / contain;
+}
+@counter-style triangle {
+ system: cyclic;
+ symbols: ‣;
+ suffix: " ";
+}
+@-ms-viewport {
+}
--- /dev/null
+.escape\|random\|char {
+ color: red;
+}
+.mixin\!tUp {
+ font-weight: bold;
+}
+.\34 04 {
+ background: red;
+}
+.\34 04 strong {
+ color: fuchsia;
+ font-weight: bold;
+}
+.trailingTest\+ {
+ color: red;
+}
+/* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */
+\62\6c\6f \63 \6B \0071 \000075o\74 e {
+ color: silver;
+}
+[ng\:cloak],
+ng\:form {
+ display: none;
+}
--- /dev/null
+.light {
+ color: green;
+}
+.see-the {
+ color: green;
+}
+.hide-the {
+ color: green;
+}
+.multiple-conditions-1 {
+ color: red;
+}
+.inheritance .test {
+ color: black;
+}
+.inheritance:hover {
+ color: pink;
+}
+.clsWithGuard {
+ dispaly: none;
+}
+.dont-split-me-up {
+ width: 1px;
+ color: red;
+ height: 1px;
+}
+ + .dont-split-me-up {
+ sibling: true;
+}
+.scope-check {
+ sub-prop: 2px;
+ prop: 1px;
+}
+.scope-check-2 {
+ sub-prop: 2px;
+ prop: 1px;
+}
--- /dev/null
+@charset "utf-8";
+div {
+ color: black;
+}
+div {
+ width: 99%;
+}
+* {
+ min-width: 45em;
+}
+h1,
+h2 > a > p,
+h3 {
+ color: none;
+}
+div.class {
+ color: blue;
+}
+div#id {
+ color: green;
+}
+.class#id {
+ color: purple;
+}
+.one.two.three {
+ color: grey;
+}
+@media print {
+ * {
+ font-size: 3em;
+ }
+}
+@media screen {
+ * {
+ font-size: 10px;
+ }
+}
+@font-face {
+ font-family: 'Garamond Pro';
+}
+a:hover,
+a:link {
+ color: #999;
+}
+p,
+p:first-child {
+ text-transform: none;
+}
+q:lang(no) {
+ quotes: none;
+}
+p + h1 {
+ font-size: 2.2em;
+}
+#shorthands {
+ border: 1px solid #000;
+ font: 12px/16px Arial;
+ font: 100%/16px Arial;
+ margin: 1px 0;
+ padding: 0 auto;
+}
+#more-shorthands {
+ margin: 0;
+ padding: 1px 0 2px 0;
+ font: normal small / 20px 'Trebuchet MS', Verdana, sans-serif;
+ font: 0/0 a;
+ border-radius: 5px / 10px;
+}
+.misc {
+ -moz-border-radius: 2px;
+ display: -moz-inline-stack;
+ width: .1em;
+ background-color: #009998;
+ background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue));
+ margin: ;
+ filter: alpha(opacity=100);
+ width: auto\9;
+}
+.misc .nested-multiple {
+ multiple-semi-colons: yes;
+}
+#important {
+ color: red !important;
+ width: 100%!important;
+ height: 20px ! important;
+}
+@font-face {
+ font-family: font-a;
+}
+@font-face {
+ font-family: font-b;
+}
+.æøå {
+ margin: 0;
+}
--- /dev/null
+@charset "UTF-8";
+/* line 1, {pathimport}test.less */
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000031}}
+/* @charset "ISO-8859-1"; */
+
+/* line 23, {pathimport}test.less */
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}}
+.tst3 {
+ color: grey;
+}
+/* line 15, {path}linenumbers.less */
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\0000315}}
+.test1 {
+ color: black;
+}
+/* line 6, {path}linenumbers.less */
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\000036}}
+.test2 {
+ color: red;
+}
+@media all {
+ /* line 5, {pathimport}test.less */
+ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000035}}
+ .tst {
+ color: black;
+ }
+}
+@media all and screen {
+ /* line 7, {pathimport}test.less */
+ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000037}}
+ .tst {
+ color: red;
+ }
+ /* line 9, {pathimport}test.less */
+ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000039}}
+ .tst .tst3 {
+ color: white;
+ }
+}
+/* line 18, {pathimport}test.less */
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000318}}
+.tst2 {
+ color: white;
+}
+/* line 27, {path}linenumbers.less */
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\0000327}}
+.test {
+ color: red;
+ width: 2;
+}
--- /dev/null
+@charset "UTF-8";
+/* line 1, {pathimport}test.less */
+/* @charset "ISO-8859-1"; */
+
+/* line 23, {pathimport}test.less */
+.tst3 {
+ color: grey;
+}
+/* line 15, {path}linenumbers.less */
+.test1 {
+ color: black;
+}
+/* line 6, {path}linenumbers.less */
+.test2 {
+ color: red;
+}
+@media all {
+ /* line 5, {pathimport}test.less */
+ .tst {
+ color: black;
+ }
+}
+@media all and screen {
+ /* line 7, {pathimport}test.less */
+ .tst {
+ color: red;
+ }
+ /* line 9, {pathimport}test.less */
+ .tst .tst3 {
+ color: white;
+ }
+}
+/* line 18, {pathimport}test.less */
+.tst2 {
+ color: white;
+}
+/* line 27, {path}linenumbers.less */
+.test {
+ color: red;
+ width: 2;
+}
--- /dev/null
+@charset "UTF-8";
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000031}}
+/* @charset "ISO-8859-1"; */
+
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}}
+.tst3 {
+ color: grey;
+}
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\0000315}}
+.test1 {
+ color: black;
+}
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\000036}}
+.test2 {
+ color: red;
+}
+@media all {
+ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000035}}
+ .tst {
+ color: black;
+ }
+}
+@media all and screen {
+ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000037}}
+ .tst {
+ color: red;
+ }
+ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000039}}
+ .tst .tst3 {
+ color: white;
+ }
+}
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000318}}
+.tst2 {
+ color: white;
+}
+@media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\0000327}}
+.test {
+ color: red;
+ width: 2;
+}
--- /dev/null
+.wrap-selector {
+ color: black;
+ one: 1px;
+ four: magic-frame;
+ visible-one: visible;
+ visible-two: visible;
+}
+.wrap-selector {
+ color: red;
+ visible-one: visible;
+ visible-two: visible;
+}
+.wrap-selector {
+ color: black;
+ background: white;
+ visible-one: visible;
+ visible-two: visible;
+}
+header {
+ background: blue;
+}
+@media screen and (min-width: 1200) {
+ header {
+ background: red;
+ }
+}
+html.lt-ie9 header {
+ background: red;
+}
+.wrap-selector {
+ test: extra-wrap;
+ visible-one: visible;
+ visible-two: visible;
+}
+.wrap-selector .wrap-selector {
+ test: wrapped-twice;
+ visible-one: visible;
+ visible-two: visible;
+}
+.wrap-selector {
+ test-func: 90;
+ test-arithmetic: 18px;
+ visible-one: visible;
+ visible-two: visible;
+}
+.without-mixins {
+ b: 1;
+}
+@media (orientation: portrait) and tv {
+ .my-selector {
+ background-color: black;
+ }
+}
+@media (orientation: portrait) and widescreen and print and tv {
+ .triple-wrapped-mq {
+ triple: true;
+ }
+}
+@media (orientation: portrait) and widescreen and tv {
+ .triple-wrapped-mq {
+ triple: true;
+ }
+}
+@media (orientation: portrait) and tv {
+ .triple-wrapped-mq {
+ triple: true;
+ }
+}
+.a {
+ test: test;
+}
--- /dev/null
+.a,
+.b,
+.c {
+ color: black;
+}
+.f,
+.e,
+.d {
+ color: black;
+}
+.g.h,
+.i.j.h,
+.k.j.h {
+ color: black;
+}
+.i.j,
+.k.j {
+ color: white;
+}
+.l,
+.m,
+.n,
+.o,
+.p,
+.q,
+.r,
+.s,
+.t {
+ color: black;
+}
+.u,
+.v.u.v {
+ color: black;
+}
+.w,
+.v.w.v {
+ color: black;
+}
+.x,
+.y,
+.z {
+ color: x;
+}
+.y,
+.z,
+.x {
+ color: y;
+}
+.z,
+.x,
+.y {
+ color: z;
+}
+.va,
+.vb,
+.vc {
+ color: black;
+}
+.vb,
+.vc {
+ color: white;
+}
+@media tv {
+ .ma,
+ .mb,
+ .mc {
+ color: black;
+ }
+ .md,
+ .ma,
+ .mb,
+ .mc {
+ color: white;
+ }
+}
+@media tv and plasma {
+ .me,
+ .mf {
+ background: red;
+ }
+}
--- /dev/null
+.clearfix,
+.foo,
+.bar {
+ *zoom: 1;
+}
+.clearfix:after,
+.foo:after,
+.bar:after {
+ content: '';
+ display: block;
+ clear: both;
+ height: 0;
+}
+.foo {
+ color: red;
+}
+.bar {
+ color: blue;
+}
--- /dev/null
+.replace.replace .replace,
+.c.replace + .replace .replace,
+.replace.replace .c,
+.c.replace + .replace .c,
+.rep_ace {
+ prop: copy-paste-replace;
+}
+.a .b .c {
+ prop: not_effected;
+}
+.a,
+.effected {
+ prop: is_effected;
+}
+.a .b {
+ prop: not_effected;
+}
+.a .b.c {
+ prop: not_effected;
+}
+.c .b .a,
+.a .b .a,
+.c .a .a,
+.a .a .a,
+.c .b .c,
+.a .b .c,
+.c .a .c,
+.a .a .c {
+ prop: not_effected;
+}
+.e.e,
+.dbl {
+ prop: extend-double;
+}
+.e.e:hover {
+ hover: not-extended;
+}
--- /dev/null
+.ext1 .ext2,
+.all .ext2 {
+ background: black;
+}
+@media tv {
+ .ext1 .ext3,
+ .tv-lowres .ext3,
+ .all .ext3 {
+ color: white;
+ }
+ .tv-lowres {
+ background: blue;
+ }
+}
+@media tv and hires {
+ .ext1 .ext4,
+ .tv-hires .ext4,
+ .all .ext4 {
+ color: green;
+ }
+ .tv-hires {
+ background: red;
+ }
+}
--- /dev/null
+.sidebar,
+.sidebar2,
+.type1 .sidebar3,
+.type2.sidebar4 {
+ width: 300px;
+ background: red;
+}
+.sidebar .box,
+.sidebar2 .box,
+.type1 .sidebar3 .box,
+.type2.sidebar4 .box {
+ background: #FFF;
+ border: 1px solid #000;
+ margin: 10px 0;
+}
+.sidebar2 {
+ background: blue;
+}
+.type1 .sidebar3 {
+ background: green;
+}
+.type2.sidebar4 {
+ background: red;
+}
+.button,
+.submit {
+ color: black;
+}
+.button:hover,
+.submit:hover {
+ color: white;
+}
+.button2 :hover {
+ nested: white;
+}
+.button2 :hover {
+ notnested: black;
+}
+.amp-test-h,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g,
+.amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g {
+ test: extended by masses of selectors;
+}
--- /dev/null
+.error,
+.badError {
+ border: 1px #f00;
+ background: #fdd;
+}
+.error.intrusion,
+.badError.intrusion {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+.intrusion .error,
+.intrusion .badError {
+ display: none;
+}
+.badError {
+ border-width: 3px;
+}
+.foo .bar,
+.foo .baz,
+.ext1 .ext2 .bar,
+.ext1 .ext2 .baz,
+.ext3 .bar,
+.ext3 .baz,
+.ext4 .bar,
+.ext4 .baz {
+ display: none;
+}
+div.ext5,
+.ext6 > .ext5,
+div.ext7,
+.ext6 > .ext7 {
+ width: 100px;
+}
+.ext,
+.a .c,
+.b .c {
+ test: 1;
+}
+.a,
+.b {
+ test: 2;
+}
+.a .c,
+.b .c {
+ test: 3;
+}
+.a .c .d,
+.b .c .d {
+ test: 4;
+}
+.replace.replace .replace,
+.c.replace + .replace .replace,
+.replace.replace .c,
+.c.replace + .replace .c,
+.rep_ace.rep_ace .rep_ace,
+.c.rep_ace + .rep_ace .rep_ace,
+.rep_ace.rep_ace .c,
+.c.rep_ace + .rep_ace .c {
+ prop: copy-paste-replace;
+}
+.attributes [data="test"],
+.attributes .attributes .attribute-test {
+ extend: attributes;
+}
+.attributes [data],
+.attributes .attributes .attribute-test2 {
+ extend: attributes2;
+}
+.attributes [data="test3"],
+.attributes .attributes .attribute-test {
+ extend: attributes2;
+}
+.header .header-nav,
+.footer .footer-nav {
+ background: red;
+}
+.header .header-nav:before,
+.footer .footer-nav:before {
+ background: blue;
+}
--- /dev/null
+.error,
+.badError {
+ border: 1px #f00;
+ background: #fdd;
+}
+.error.intrusion,
+.badError.intrusion {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+.intrusion .error,
+.intrusion .badError {
+ display: none;
+}
+.badError {
+ border-width: 3px;
+}
+.foo .bar,
+.foo .baz,
+.ext1 .ext2 .bar,
+.ext1 .ext2 .baz,
+.ext3 .bar,
+.ext3 .baz,
+.foo .ext3,
+.ext4 .bar,
+.ext4 .baz,
+.foo .ext4 {
+ display: none;
+}
+div.ext5,
+.ext6 > .ext5,
+div.ext7,
+.ext6 > .ext7 {
+ width: 100px;
+}
+.ext8.ext9,
+.fuu {
+ result: add-foo;
+}
+.ext8 .ext9,
+.ext8 + .ext9,
+.ext8 > .ext9,
+.buu,
+.zap,
+.zoo {
+ result: bar-matched;
+}
+.ext8.nomatch {
+ result: none;
+}
+.ext8 .ext9,
+.buu {
+ result: match-nested-bar;
+}
+.ext8.ext9,
+.fuu {
+ result: match-nested-foo;
+}
+.aa,
+.cc {
+ color: black;
+}
+.aa .dd,
+.aa .ee {
+ background: red;
+}
+.bb,
+.cc,
+.ee,
+.ff {
+ background: red;
+}
+.bb .bb,
+.ff .ff {
+ color: black;
+}
--- /dev/null
+.multiunit {
+ length: 6;
+ extract: abc "abc" 1 1px 1% #112233;
+}
+.incorrect-index {
+ v1: extract(a b c, 5);
+ v2: extract(a, b, c, -2);
+}
+.scalar {
+ var-value: variable;
+ var-length: 1;
+ ill-index: extract(variable, 2);
+ name-value: name;
+ string-value: "string";
+ number-value: 12345678;
+ color-value: blue;
+ rgba-value: rgba(80, 160, 240, 0.67);
+ empty-value: ;
+ name-length: 1;
+ string-length: 1;
+ number-length: 1;
+ color-length: 1;
+ rgba-length: 1;
+ empty-length: 1;
+}
+.mixin-arguments-1 {
+ length: 4;
+ extract: c | b | a;
+}
+.mixin-arguments-2 {
+ length: 4;
+ extract: c | b | a;
+}
+.mixin-arguments-3 {
+ length: 4;
+ extract: c | b | a;
+}
+.mixin-arguments-4 {
+ length: 0;
+ extract: extract(, 2) | extract(, 1);
+}
+.mixin-arguments-2 {
+ length: 4;
+ extract: c | b | a;
+}
+.mixin-arguments-3 {
+ length: 4;
+ extract: c | b | a;
+}
+.mixin-arguments-4 {
+ length: 3;
+ extract: c | b;
+}
+.mixin-arguments-2 {
+ length: 4;
+ extract: 3 | 2 | 1;
+}
+.mixin-arguments-3 {
+ length: 4;
+ extract: 3 | 2 | 1;
+}
+.mixin-arguments-4 {
+ length: 3;
+ extract: 3 | 2;
+}
+.md-space-comma {
+ length-1: 3;
+ extract-1: 1 2 3;
+ length-2: 3;
+ extract-2: 2;
+}
+.md-space-comma-as-args-2 {
+ length: 3;
+ extract: "x" "y" "z" | 1 2 3 | a b c;
+}
+.md-space-comma-as-args-3 {
+ length: 3;
+ extract: "x" "y" "z" | 1 2 3 | a b c;
+}
+.md-space-comma-as-args-4 {
+ length: 2;
+ extract: "x" "y" "z" | 1 2 3;
+}
+.md-cat-space-comma {
+ length-1: 3;
+ extract-1: 1 2 3;
+ length-2: 3;
+ extract-2: 2;
+}
+.md-cat-space-comma-as-args-2 {
+ length: 3;
+ extract: "x" "y" "z" | 1 2 3 | a b c;
+}
+.md-cat-space-comma-as-args-3 {
+ length: 3;
+ extract: "x" "y" "z" | 1 2 3 | a b c;
+}
+.md-cat-space-comma-as-args-4 {
+ length: 2;
+ extract: "x" "y" "z" | 1 2 3;
+}
+.md-cat-comma-space {
+ length-1: 3;
+ extract-1: 1, 2, 3;
+ length-2: 3;
+ extract-2: 2;
+}
+.md-cat-comma-space-as-args-1 {
+ length: 3;
+ extract: "x", "y", "z" | 1, 2, 3 | a, b, c;
+}
+.md-cat-comma-space-as-args-2 {
+ length: 3;
+ extract: "x", "y", "z" | 1, 2, 3 | a, b, c;
+}
+.md-cat-comma-space-as-args-3 {
+ length: 3;
+ extract: "x", "y", "z" | 1, 2, 3 | a, b, c;
+}
+.md-cat-comma-space-as-args-4 {
+ length: 0;
+ extract: extract(, 2) | extract(, 1);
+}
+.md-3D {
+ length-1: 2;
+ extract-1: a b c d, 1 2 3 4;
+ length-2: 2;
+ extract-2: 5 6 7 8;
+ length-3: 4;
+ extract-3: 7;
+ length-4: 1;
+ extract-4: 8;
+}
--- /dev/null
+#functions {
+ color: #660000;
+ width: 16;
+ height: undefined("self");
+ border-width: 5;
+ variable: 11;
+ background: linear-gradient(#000000, #ffffff);
+}
+#built-in {
+ escaped: -Some::weird(#thing, y);
+ lighten: #ffcccc;
+ darken: #330000;
+ saturate: #203c31;
+ desaturate: #29332f;
+ greyscale: #2e2e2e;
+ hsl-clamp: #ffffff;
+ spin-p: #bf6a40;
+ spin-n: #bf4055;
+ luma-white: 100%;
+ luma-black: 0%;
+ luma-black-alpha: 0%;
+ luma-red: 21.26%;
+ luma-green: 71.52%;
+ luma-blue: 7.22%;
+ luma-yellow: 92.78%;
+ luma-cyan: 78.74%;
+ luma-differs-from-luminance: 23.89833349%;
+ luminance-white: 100%;
+ luminance-black: 0%;
+ luminance-black-alpha: 0%;
+ luminance-red: 21.26%;
+ luminance-differs-from-luma: 36.40541176%;
+ contrast-filter: contrast(30%);
+ saturate-filter: saturate(5%);
+ contrast-white: #000000;
+ contrast-black: #ffffff;
+ contrast-red: #ffffff;
+ contrast-green: #000000;
+ contrast-blue: #ffffff;
+ contrast-yellow: #000000;
+ contrast-cyan: #000000;
+ contrast-light: #111111;
+ contrast-dark: #eeeeee;
+ contrast-wrongorder: #111111;
+ contrast-light-thresh: #111111;
+ contrast-dark-thresh: #eeeeee;
+ contrast-high-thresh: #eeeeee;
+ contrast-low-thresh: #111111;
+ contrast-light-thresh-per: #111111;
+ contrast-dark-thresh-per: #eeeeee;
+ contrast-high-thresh-per: #eeeeee;
+ contrast-low-thresh-per: #111111;
+ replace: "Hello, World!";
+ replace-captured: "This is a new string.";
+ replace-with-flags: "2 + 2 = 4";
+ replace-single-quoted: 'foo-2';
+ replace-escaped-string: bar-2;
+ replace-keyword: baz-2;
+ format: "rgb(32, 128, 64)";
+ format-string: "hello world";
+ format-multiple: "hello earth 2";
+ format-url-encode: "red is %23ff0000";
+ format-single-quoted: 'hello single world';
+ format-escaped-string: hello escaped world;
+ eformat: rgb(32, 128, 64);
+ unitless: 12;
+ unit: 14em;
+ unitpercentage: 100%;
+ get-unit: px;
+ get-unit-empty: ;
+ hue: 98;
+ saturation: 12%;
+ lightness: 95%;
+ hsvhue: 98;
+ hsvsaturation: 12%;
+ hsvvalue: 95%;
+ red: 255;
+ green: 255;
+ blue: 255;
+ rounded: 11;
+ rounded-two: 10.67;
+ roundedpx: 3px;
+ roundedpx-three: 3.333px;
+ rounded-percentage: 10%;
+ ceil: 11px;
+ floor: 12px;
+ sqrt: 5px;
+ pi: 3.14159265;
+ mod: 2m;
+ abs: 4%;
+ tan: 0.90040404;
+ sin: 0.17364818;
+ cos: 0.84385396;
+ atan: 0.1rad;
+ atan: 34deg;
+ atan: 45deg;
+ pow: 64px;
+ pow: 64;
+ pow: 27;
+ min: 0;
+ min: 5;
+ min: 1pt;
+ min: 3mm;
+ max: 3;
+ max: 5em;
+ percentage: 20%;
+ color-quoted-digit: #dda0dd;
+ color-quoted-keyword: #dda0dd;
+ color-color: #dda0dd;
+ color-keyword: #dda0dd;
+ tint: #898989;
+ tint-full: #ffffff;
+ tint-percent: #898989;
+ tint-negative: #656565;
+ shade: #686868;
+ shade-full: #000000;
+ shade-percent: #686868;
+ shade-negative: #868686;
+ fade-out: rgba(255, 0, 0, 0.95);
+ fade-in: rgba(255, 0, 0, 0.95);
+ hsv: #4d2926;
+ hsva: rgba(77, 40, 38, 0.2);
+ mix: #ff3300;
+ mix-0: #ffff00;
+ mix-100: #ff0000;
+ mix-weightless: #ff8000;
+ mixt: rgba(255, 0, 0, 0.5);
+}
+#built-in .is-a {
+ ruleset: true;
+ color: true;
+ color1: true;
+ color2: true;
+ color3: true;
+ keyword: true;
+ number: true;
+ string: true;
+ pixel: true;
+ percent: true;
+ em: true;
+ cat: true;
+ no-unit-is-empty: true;
+ case-insensitive-1: true;
+ case-insensitive-2: true;
+}
+#alpha {
+ alpha: rgba(153, 94, 51, 0.6);
+ alpha2: 0.5;
+ alpha3: 0;
+}
+#blendmodes {
+ multiply: #ed0000;
+ screen: #f600f6;
+ overlay: #ed0000;
+ softlight: #fa0000;
+ hardlight: #0000ed;
+ difference: #f600f6;
+ exclusion: #f600f6;
+ average: #7b007b;
+ negation: #d73131;
+}
+#extract-and-length {
+ extract: 3 2 1 C B A;
+ length: 6;
+}
+#quoted-functions-in-mixin {
+ replace-double-quoted: 'foo-2';
+ replace-single-quoted: 'foo-4';
+ replace-escaped-string: bar-2;
+ replace-keyword: baz-2;
+ replace-anonymous: qux-2;
+ format-double-quoted: "hello world";
+ format-single-quoted: 'hello single world';
+ format-escaped-string: hello escaped world;
+ format-keyword: hello;
+ format-anonymous: hello anonymous world;
+}
+#list-details {
+ length: 2;
+ one: a 1;
+ two: b 2;
+ two-length: 2;
+ two-one: b;
+ two-two: 2;
+}
--- /dev/null
+/**
+ * Test
+ */
+#header {
+ color: #333333;
+ border-left: 1px;
+ border-right: 2px;
+}
+#footer {
+ color: #114411;
+ border-color: #f20d0d;
+}
--- /dev/null
+/**
+ * Test
+ */
+.class {
+ color: red;
+}
--- /dev/null
+.nav {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=20);
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="#000000", GradientType=0);
+}
+.evalTest1 {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=30);
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=5);
+}
--- /dev/null
+#import {
+ color: red;
+}
+@media (min-width: 600px) {
+ #css { color: yellow; }
+
+}
+this isn't very valid CSS.
--- /dev/null
+body {
+ width: 100%;
+}
+#logo {
+ width: 100px;
+ height: 100px;
+ background: url('../assets/logo.png');
+ background: url("#inline-svg");
+}
+
+.a {
+ var: test;
+}
--- /dev/null
+#import {
+ color: red;
+}
+body {
+ width: 100%;
+}
+.test-f {
+ height: 10px;
+}
+body {
+ width: 100%;
+}
+.test-f {
+ height: 10px;
+}
--- /dev/null
+input[type="text"].class#id[attr=32]:not(1) {
+ color: white;
+}
+div#id.class[a=1][b=2].class:not(1) {
+ color: white;
+}
+@media print {
+ .class {
+ color: blue;
+ }
+ .class .sub {
+ width: 42;
+ }
+}
+.visible {
+ color: red;
+}
+.visible .c {
+ color: green;
+}
+.visible {
+ color: green;
+}
+.visible:hover {
+ color: green;
+}
+.only-with-visible + .visible,
+.visible + .only-with-visible,
+.visible + .visible {
+ color: green;
+}
+.only-with-visible + .visible .sub,
+.visible + .only-with-visible .sub,
+.visible + .visible .sub {
+ color: green;
+}
+@supports (something: else) {
+ .class {
+ something: else;
+ }
+ .nestedToo .class {
+ something: else;
+ }
+}
+.b {
+ color: red;
+ color: green;
+}
+.b .c {
+ color: green;
+}
+.b:hover {
+ color: green;
+}
+.b + .b {
+ color: green;
+}
+.b + .b .sub {
+ color: green;
+}
+.y {
+ pulled-in: yes;
+}
+/* comment pulled in */
+.visible {
+ extend: test;
+}
+.test-mediaq-import {
+ color: green;
+ test: 340px;
+}
+@media (max-size: 450px) {
+ .test-mediaq-import {
+ color: red;
+ }
+}
+.test {
+ color: red;
+}
+.test:first-child {
+ color: blue;
+}
+@keyframes some-name {
+ property: value;
+}
+@supports (animation-name: test) {
+ @keyframes some-name {
+ property: value;
+ }
+ .selector {
+ color: red;
+ }
+}
--- /dev/null
+@charset "UTF-8";
+@import url(http://fonts.googleapis.com/css?family=Open+Sans);
+@import url(/absolute/something.css) screen and (color) and (max-width: 600px);
+@import url("//ha.com/file.css") (min-width: 100px);
+#import-test {
+ height: 10px;
+ color: red;
+ width: 10px;
+ height: 30%;
+}
+@media screen and (max-width: 600px) {
+ body {
+ width: 100%;
+ }
+}
+#import {
+ color: red;
+}
+.mixin {
+ height: 10px;
+ color: red;
+}
+.deep-import-url {
+ color: red;
+}
+@media screen and (max-width: 601px) {
+ #css {
+ color: yellow;
+ }
+}
+@media screen and (max-width: 602px) {
+ body {
+ width: 100%;
+ }
+}
+@media screen and (max-width: 603px) {
+ #css {
+ color: yellow;
+ }
+}
+@media print {
+ body {
+ width: 100%;
+ }
+}
--- /dev/null
+body {
+ width: 100%;
+}
+data-uri {
+ property: url("data:image/svg+xml,%3Csvg%20height%3D%22100%22%20width%3D%22100%22%3E%0A%20%20%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20stroke%3D%22black%22%20stroke-width%3D%221%22%20fill%3D%22blue%22%20%2F%3E%0A%3C%2Fsvg%3E");
+}
--- /dev/null
+.eval {
+ js: 42;
+ js: 2;
+ js: "hello world";
+ js: 1, 2, 3;
+ title: "string";
+ ternary: true;
+ multiline: 2;
+}
+.scope {
+ var: 42;
+ escaped: 7px;
+}
+.vars {
+ width: 8;
+}
+.escape-interpol {
+ width: hello world;
+}
+.arrays {
+ ary: "1, 2, 3";
+ ary1: "1, 2, 3";
+}
+.test-tran {
+ 1: opacity 0.3s ease-in 0.3s, max-height 0.6s linear, margin-bottom 0.4s linear;
+ 2: [opacity 0.3s ease-in 0.3s, max-height 0.6s linear, margin-bottom 0.4s linear];
+ 3: opacity 0.3s ease-in 0.3s, max-height 0.6s linear, margin-bottom 0.4s linear;
+}
--- /dev/null
+.lazy-eval {
+ width: 100%;
+}
--- /dev/null
+@media (-o-min-device-pixel-ratio: 2/1) {
+ .test-math-and-units {
+ font: ignores 0/0 rules;
+ test-division: 7em;
+ simple: 2px;
+ }
+}
--- /dev/null
+@media print {
+ .class {
+ color: blue;
+ }
+ .class .sub {
+ width: 42;
+ }
+ .top,
+ header > h1 {
+ color: #444444;
+ }
+}
+@media screen {
+ body {
+ max-width: 480;
+ }
+}
+@media all and (device-aspect-ratio: 16 / 9) {
+ body {
+ max-width: 800px;
+ }
+}
+@media all and (orientation: portrait) {
+ aside {
+ float: none;
+ }
+}
+@media handheld and (min-width: 42), screen and (min-width: 20em) {
+ body {
+ max-width: 480px;
+ }
+}
+@media print {
+ body {
+ padding: 20px;
+ }
+ body header {
+ background-color: red;
+ }
+}
+@media print and (orientation: landscape) {
+ body {
+ margin-left: 20px;
+ }
+}
+@media screen {
+ .sidebar {
+ width: 300px;
+ }
+}
+@media screen and (orientation: landscape) {
+ .sidebar {
+ width: 500px;
+ }
+}
+@media a and b {
+ .first .second .third {
+ width: 300px;
+ }
+ .first .second .fourth {
+ width: 3;
+ }
+}
+@media a and b and c {
+ .first .second .third {
+ width: 500px;
+ }
+}
+@media a, b and c {
+ body {
+ width: 95%;
+ }
+}
+@media a and x, b and c and x, a and y, b and c and y {
+ body {
+ width: 100%;
+ }
+}
+.a {
+ background: black;
+}
+@media handheld {
+ .a {
+ background: white;
+ }
+}
+@media handheld and (max-width: 100px) {
+ .a {
+ background: red;
+ }
+}
+.b {
+ background: black;
+}
+@media handheld {
+ .b {
+ background: white;
+ }
+}
+@media handheld and (max-width: 200px) {
+ .b {
+ background: red;
+ }
+}
+@media only screen and (max-width: 200px) {
+ body {
+ width: 480px;
+ }
+}
+@media print {
+ @page :left {
+ margin: 0.5cm;
+ }
+ @page :right {
+ margin: 0.5cm;
+ }
+ @page Test:first {
+ margin: 1cm;
+ }
+ @page :first {
+ size: 8.5in 11in;
+
+ @top-left {
+ margin: 1cm;
+ }
+ @top-left-corner {
+ margin: 1cm;
+ }
+ @top-center {
+ margin: 1cm;
+ }
+ @top-right {
+ margin: 1cm;
+ }
+ @top-right-corner {
+ margin: 1cm;
+ }
+ @bottom-left {
+ margin: 1cm;
+ }
+ @bottom-left-corner {
+ margin: 1cm;
+ }
+ @bottom-center {
+ margin: 1cm;
+ }
+ @bottom-right {
+ margin: 1cm;
+ }
+ @bottom-right-corner {
+ margin: 1cm;
+ }
+ @left-top {
+ margin: 1cm;
+ }
+ @left-middle {
+ margin: 1cm;
+ }
+ @left-bottom {
+ margin: 1cm;
+ }
+ @right-top {
+ margin: 1cm;
+ }
+ @right-middle {
+ content: "Page " counter(page);
+ }
+ @right-bottom {
+ margin: 1cm;
+ }
+ }
+}
+@media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 128dpcm) {
+ .b {
+ background: red;
+ }
+}
+body {
+ background: red;
+}
+@media (max-width: 500px) {
+ body {
+ background: green;
+ }
+}
+@media (max-width: 1000px) {
+ body {
+ background: red;
+ background: blue;
+ }
+}
+@media (max-width: 1000px) and (max-width: 500px) {
+ body {
+ background: green;
+ }
+}
+@media (max-width: 1200px) {
+ /* a comment */
+}
+@media (max-width: 1200px) and (max-width: 900px) {
+ body {
+ font-size: 11px;
+ }
+}
+@media (min-width: 480px) {
+ .nav-justified > li {
+ display: table-cell;
+ }
+}
+@media (min-width: 768px) and (min-width: 480px) {
+ .menu > li {
+ display: table-cell;
+ }
+}
+@media all and tv {
+ .all-and-tv-variables {
+ var: all-and-tv;
+ }
+}
--- /dev/null
+.test1 {
+ transform: rotate(90deg), skew(30deg), scale(2, 4);
+}
+.test2 {
+ transform: rotate(90deg), skew(30deg);
+ transform: scaleX(45deg);
+}
+.test3 {
+ transform: scaleX(45deg);
+ background: url(data://img1.png);
+}
+.test4 {
+ transform: rotate(90deg), skew(30deg);
+ transform: scale(2, 4) !important;
+}
+.test5 {
+ transform: rotate(90deg), skew(30deg);
+ transform: scale(2, 4) !important;
+}
+.test6 {
+ transform: scale(2, 4);
+}
+.test-interleaved {
+ transform: t1, t2, t3;
+ background: b1, b2, b3;
+}
+.test-spaced {
+ transform: t1 t2 t3;
+ background: b1 b2, b3;
+}
+.test-interleaved-with-spaced {
+ transform: t1s, t2 t3s, t4 t5s t6s;
+ background: b1 b2s, b3, b4;
+}
--- /dev/null
+#hidden {
+ color: transparent;
+}
+#hidden1 {
+ color: transparent;
+}
+.two-args {
+ color: blue;
+ width: 10px;
+ height: 99%;
+ border: 2px dotted black;
+}
+.one-arg {
+ width: 15px;
+ height: 49%;
+}
+.no-parens {
+ width: 5px;
+ height: 49%;
+}
+.no-args {
+ width: 5px;
+ height: 49%;
+}
+.var-args {
+ width: 45;
+ height: 17%;
+}
+.multi-mix {
+ width: 10px;
+ height: 29%;
+ margin: 4;
+ padding: 5;
+}
+body {
+ padding: 30px;
+ color: #ff0000;
+}
+.scope-mix {
+ width: 8;
+}
+.content {
+ width: 600px;
+}
+.content .column {
+ margin: 600px;
+}
+#same-var-name {
+ radius: 5px;
+}
+#var-inside {
+ width: 10px;
+}
+.arguments {
+ border: 1px solid black;
+ width: 1px;
+}
+.arguments2 {
+ border: 0px;
+ width: 0px;
+}
+.arguments3 {
+ border: 0px;
+ width: 0px;
+}
+.arguments4 {
+ border: 0 1 2 3 4;
+ rest: 1 2 3 4;
+ width: 0;
+}
+.edge-case {
+ border: "{";
+ width: "{";
+}
+.slash-vs-math {
+ border-radius: 2px/5px;
+ border-radius: 5px/10px;
+ border-radius: 6px;
+}
+.comma-vs-semi-colon {
+ one: a;
+ two: b, c;
+ one: d, e;
+ two: f;
+ one: g;
+ one: h;
+ one: i;
+ one: j;
+ one: k;
+ two: l;
+ one: m, n;
+ one: o, p;
+ two: q;
+ one: r, s;
+ two: t;
+}
+#named-conflict {
+ four: a, 11, 12, 13;
+ four: a, 21, 22, 23;
+}
+.test-mixin-default-arg {
+ defaults: 1px 1px 1px;
+ defaults: 2px 2px 2px;
+}
+.selector {
+ margin: 2, 2, 2, 2;
+}
+.selector2 {
+ margin: 2, 2, 2, 2;
+}
+.selector3 {
+ margin: 4;
+}
--- /dev/null
+.class {
+ width: 99px;
+}
+.overwrite {
+ width: 99px;
+}
+.nested .class {
+ width: 5px;
+}
--- /dev/null
+guard-default-basic-1-1 {
+ case: 1;
+}
+guard-default-basic-1-2 {
+ default: 2;
+}
+guard-default-basic-2-0 {
+ default: 0;
+}
+guard-default-basic-2-2 {
+ case: 2;
+}
+guard-default-basic-3-0 {
+ default: 0;
+}
+guard-default-basic-3-2 {
+ case: 2;
+}
+guard-default-basic-3-3 {
+ case: 3;
+}
+guard-default-definition-order-0 {
+ default: 0;
+}
+guard-default-definition-order-2 {
+ case: 2;
+}
+guard-default-definition-order-2 {
+ case: 3;
+}
+guard-default-out-of-guard-0 {
+ case-0: default();
+ case-1: 1;
+ default: 2;
+ case-2: default();
+}
+guard-default-out-of-guard-1 {
+ default: default();
+}
+guard-default-out-of-guard-2 {
+ default: default();
+}
+guard-default-expr-not-1 {
+ case: 1;
+ default: 1;
+}
+guard-default-expr-eq-true {
+ case: true;
+}
+guard-default-expr-eq-false {
+ case: false;
+ default: false;
+}
+guard-default-expr-or-1 {
+ case: 1;
+}
+guard-default-expr-or-2 {
+ case: 2;
+ default: 2;
+}
+guard-default-expr-or-3 {
+ default: 3;
+}
+guard-default-expr-and-1 {
+ case: 1;
+}
+guard-default-expr-and-2 {
+ case: 2;
+}
+guard-default-expr-and-3 {
+ default: 3;
+}
+guard-default-expr-always-1 {
+ case: 1;
+ default: 1;
+}
+guard-default-expr-always-2 {
+ default: 2;
+}
+guard-default-expr-never-1 {
+ case: 1;
+}
+guard-default-multi-1-0 {
+ case: 0;
+}
+guard-default-multi-1-1 {
+ default-1: 1;
+}
+guard-default-multi-2-1 {
+ default-1: no;
+}
+guard-default-multi-2-2 {
+ default-2: no;
+}
+guard-default-multi-2-3 {
+ default-3: 3;
+}
+guard-default-multi-3-blue {
+ case-2: darkblue;
+}
+guard-default-multi-3-green {
+ default-color: green;
+}
+guard-default-multi-3-foo {
+ case-1: I am 'foo';
+}
+guard-default-multi-3-baz {
+ default-string: I am 'baz';
+}
+guard-default-multi-4 {
+ always: 1;
+ always: 2;
+ case: 2;
+}
+guard-default-not-ambiguos-2 {
+ case: 1;
+ not-default: 2;
+}
+guard-default-not-ambiguos-3 {
+ case: 1;
+ not-default-1: 2;
+ not-default-2: 2;
+}
+guard-default-scopes-3 {
+ 3: when default;
+}
+guard-default-scopes-1 {
+ 1: no condition;
+}
--- /dev/null
+.light1 {
+ color: white;
+ margin: 1px;
+}
+.light2 {
+ color: black;
+ margin: 1px;
+}
+.max1 {
+ width: 6;
+}
+.max2 {
+ width: 8;
+}
+.glob1 {
+ margin: auto auto;
+}
+.ops1 {
+ height: gt-or-eq;
+ height: lt-or-eq;
+ height: lt-or-eq-alias;
+}
+.ops2 {
+ height: gt-or-eq;
+ height: not-eq;
+}
+.ops3 {
+ height: lt-or-eq;
+ height: lt-or-eq-alias;
+ height: not-eq;
+}
+.default1 {
+ content: default;
+}
+.test1 {
+ content: "true.";
+}
+.test2 {
+ content: "false.";
+}
+.test3 {
+ content: "false.";
+}
+.test4 {
+ content: "false.";
+}
+.test5 {
+ content: "false.";
+}
+.bool1 {
+ content: true and true;
+ content: true;
+ content: false, true;
+ content: false and true and true, true;
+ content: false, true and true;
+ content: false, false, true;
+ content: false, true and true and true, false;
+ content: not false;
+ content: not false and false, not false;
+}
+.equality-units {
+ test: pass;
+}
+.colorguardtest {
+ content: is red;
+ content: is not blue its red;
+ content: is not blue its purple;
+}
+.stringguardtest {
+ content: "theme1" is "theme1";
+ content: "theme1" is not "theme2";
+ content: "theme1" is 'theme1';
+ content: "theme1" is not 'theme2';
+ content: 'theme1' is "theme1";
+ content: 'theme1' is not "theme2";
+ content: 'theme1' is 'theme1';
+ content: 'theme1' is not 'theme2';
+ content: theme1 is not "theme2";
+ content: theme1 is not 'theme2';
+ content: theme1 is theme1;
+}
+.variouse-types-comparison {
+ /**/
+ content: true is not equal to false;
+ content: false is not equal to true too;
+ /**/
+ content: 1 is not equal to true;
+ content: true is not equal to 1 too;
+ /**/
+ content: 2 is equal to 2px;
+ content: 2px is equal to 2 too;
+ /**/
+ content: 3 is equal to 3;
+ content: 3 is equal to 3 too;
+ /**/
+ content: 5 is not equal to 4;
+ content: 4 is not equal to 5 too;
+ /**/
+ content: abc is equal to abc;
+ content: abc is equal to abc too;
+ /**/
+ content: abc is not equal to "abc";
+ content: "abc" is not equal to abc too;
+ /**/
+ content: 'abc' is less than "abd";
+ content: "abd" is greater than 'abc' too;
+ content: 'abc' is not equal to "abd";
+ content: "abd" is not equal to 'abc' too;
+ /**/
+ content: 6 is equal to 6;
+ content: 6 is equal to 6 too;
+ /**/
+ content: 8 is less than 9 too;
+ content: 9 is greater than 8;
+ content: 9 is not equal to 8;
+ content: 8 is not equal to 9 too;
+ /**/
+ content: a is not equal to b;
+ content: b is not equal to a too;
+ /**/
+ content: 1 2 is not equal to 3;
+ content: 3 is not equal to 1 2 too;
+}
+.list-comparison {
+ /**/
+ content: a b c is equal to a b c;
+ content: a b c is equal to a b c too;
+ /**/
+ content: a b c is not equal to a b d;
+ content: a b d is not equal to a b c too;
+ /**/
+ content: a, b, c is equal to a, b, c;
+ content: a, b, c is equal to a, b, c too;
+ /**/
+ content: a, b, c is not equal to a, b, d;
+ content: a, b, d is not equal to a, b, c too;
+ /**/
+ content: 1 2px 300ms is equal to 1em 2 0.3s;
+ content: 1em 2 0.3s is equal to 1 2px 300ms too;
+ /**/
+ content: 1 2 3 is not equal to 1, 2, 3;
+ content: 1, 2, 3 is not equal to 1 2 3 too;
+ /**/
+ content: 1, 2, 3 is equal to 1, 2, 3;
+ content: 1, 2, 3 is equal to 1, 2, 3 too;
+ /**/
+ content: 1 2 3 1, 2, 3 is equal to 1 2 3 1, 2, 3;
+ content: 1 2 3 1, 2, 3 is equal to 1 2 3 1, 2, 3 too;
+ /**/
+ content: 1 2 3 1, 2, 3 is not equal to 1, 2, 3 1 2 3;
+ content: 1, 2, 3 1 2 3 is not equal to 1 2 3 1, 2, 3 too;
+ /**/
+ content: 1 2 3 1, 2, 3 4 is equal to 1 2 3 1, 2, 3 4;
+ content: 1 2 3 1, 2, 3 4 is equal to 1 2 3 1, 2, 3 4 too;
+}
+#tryNumberPx {
+ catch: all;
+ declare: 4;
+ declare: 4px;
+}
+.call-lock-mixin .call-inner-lock-mixin {
+ a: 1;
+ x: 1;
+}
+.mixin-generated-class {
+ a: 1;
+}
+#guarded-caller {
+ guarded: namespace;
+ silent: namespace;
+ guarded: with default;
+}
+#guarded-deeper {
+ should: match 1;
+}
--- /dev/null
+.class {
+ border: 1;
+ boxer: 1;
+ border-width: 1;
+ border: 2 !important;
+ boxer: 2 !important;
+ border-width: 2 !important;
+ border: 3;
+ boxer: 3;
+ border-width: 3;
+ border: 4 !important;
+ boxer: 4 !important;
+ border-width: 4 !important;
+ border: 5;
+ boxer: 5;
+ border-width: 5;
+ border: 0 !important;
+ boxer: 0 !important;
+ border-width: 0 !important;
+ border: 9 !important;
+ border: 9;
+ boxer: 9;
+ border-width: 9;
+}
+.class .inner {
+ test: 1;
+}
+.class .inner {
+ test: 2 !important;
+}
+.class .inner {
+ test: 3;
+}
+.class .inner {
+ test: 4 !important;
+}
+.class .inner {
+ test: 5;
+}
+.class .inner {
+ test: 0 !important;
+}
+.class .inner {
+ test: 9;
+}
+.when-calling-nested-issue-2394 {
+ width: auto !important;
+}
+.when-calling-nested-with-param-issue-2394 {
+ width: 10px !important;
+}
+.class1-2421 {
+ margin: 5px !important;
+}
+.class2-2421 {
+ margin: 5px;
+}
--- /dev/null
+.123 {
+ a: 0;
+}
+.foo {
+ a: 1;
+}
+.foo {
+ a: 2;
+}
+#foo {
+ a: 3;
+}
+#foo {
+ a: 4;
+}
+mi-test-a {
+ a: 0;
+ a: 1;
+ a: 2;
+ a: 3;
+ a: 4;
+}
+.b .bb.foo-xxx .yyy-foo#foo .foo.bbb {
+ b: 1;
+}
+mi-test-b {
+ b: 1;
+}
+#foo-foo > .bar .baz {
+ c: c;
+}
+mi-test-c-1 > .bar .baz {
+ c: c;
+}
+mi-test-c-2 .baz {
+ c: c;
+}
+mi-test-c-3 {
+ c: c;
+}
+mi-test-d {
+ gender: "Male";
+}
--- /dev/null
+.named-arg {
+ color: blue;
+ width: 5px;
+ height: 99%;
+ args: 1px 100%;
+ text-align: center;
+}
+.class {
+ width: 5px;
+ height: 19%;
+ args: 1px 20%;
+}
+.all-args-wrong-args {
+ width: 10px;
+ height: 9%;
+ args: 2px 10%;
+}
+.named-args2 {
+ width: 15px;
+ height: 49%;
+ color: #646464;
+}
+.named-args3 {
+ width: 5px;
+ height: 29%;
+ color: #123456;
+}
--- /dev/null
+.class .inner {
+ height: 300;
+}
+.class .inner .innest {
+ width: 30;
+ border-width: 60;
+}
+.class2 .inner {
+ height: 600;
+}
+.class2 .inner .innest {
+ width: 60;
+ border-width: 120;
+}
--- /dev/null
+.zero {
+ variadic: true;
+ named-variadic: true;
+ zero: 0;
+ one: 1;
+ two: 2;
+ three: 3;
+}
+.one {
+ variadic: true;
+ named-variadic: true;
+ one: 1;
+ one-req: 1;
+ two: 2;
+ three: 3;
+}
+.two {
+ variadic: true;
+ named-variadic: true;
+ two: 2;
+ three: 3;
+}
+.three {
+ variadic: true;
+ named-variadic: true;
+ three-req: 3;
+ three: 3;
+}
+.left {
+ left: 1;
+}
+.right {
+ right: 1;
+}
+.border-right {
+ color: black;
+ border-right: 4px;
+}
+.border-left {
+ color: black;
+ border-left: 4px;
+}
+.only-right {
+ right: 33;
+}
+.only-left {
+ left: 33;
+}
+.left-right {
+ both: 330;
+}
--- /dev/null
+.mixin {
+ border: 1px solid black;
+}
+.mixout {
+ border-color: orange;
+}
+.borders {
+ border-style: dashed;
+}
+.mixin > * {
+ border: do not match me;
+}
+#namespace .borders {
+ border-style: dotted;
+}
+#namespace .biohazard {
+ content: "death";
+}
+#namespace .biohazard .man {
+ color: transparent;
+}
+#theme > .mixin {
+ background-color: grey;
+}
+#container {
+ color: black;
+ border: 1px solid black;
+ border-color: orange;
+ background-color: grey;
+}
+#header .milk {
+ color: white;
+ border: 1px solid black;
+ background-color: grey;
+}
+#header #cookie {
+ border-style: dashed;
+}
+#header #cookie .chips {
+ border-style: dotted;
+}
+#header #cookie .chips .calories {
+ color: black;
+ border: 1px solid black;
+ border-color: orange;
+ background-color: grey;
+}
+.secure-zone {
+ color: transparent;
+}
+.direct {
+ border-style: dotted;
+}
+.bo,
+.bar {
+ width: 100%;
+}
+.bo {
+ border: 1px;
+}
+.ar.bo.ca {
+ color: black;
+}
+.jo.ki {
+ background: none;
+}
+.amp.support {
+ color: orange;
+}
+.amp.support .higher {
+ top: 0px;
+}
+.amp.support.deeper {
+ height: auto;
+}
+.extended {
+ width: 100%;
+ border: 1px;
+ background: none;
+ color: orange;
+ top: 0px;
+ height: auto;
+}
+.extended .higher {
+ top: 0px;
+}
+.extended.deeper {
+ height: auto;
+}
+.do .re .mi .fa .sol .la .si {
+ color: cyan;
+}
+.mutli-selector-parents {
+ color: cyan;
+}
+.foo .bar {
+ width: 100%;
+}
+.underParents {
+ color: red;
+}
+.parent .underParents {
+ color: red;
+}
+* + h1 {
+ margin-top: 25px;
+}
+legend + h1 {
+ margin-top: 0;
+}
+h1 + * {
+ margin-top: 10px;
+}
+* + h2 {
+ margin-top: 20px;
+}
+legend + h2 {
+ margin-top: 0;
+}
+h2 + * {
+ margin-top: 8px;
+}
+* + h3 {
+ margin-top: 15px;
+}
+legend + h3 {
+ margin-top: 0;
+}
+h3 + * {
+ margin-top: 5px;
+}
+.error {
+ background-image: "/a.png";
+ background-position: center center;
+}
+.test-rec .recursion {
+ color: black;
+}
+.button {
+ padding-left: 44px;
+}
+.button.large {
+ padding-left: 40em;
+}
--- /dev/null
+#header {
+ color: #333333;
+ border-left: 1px;
+ border-right: 2px;
+}
+#footer {
+ color: #114411;
+ border-color: #842210;
+}
--- /dev/null
+#operations {
+ color: #111111;
+ height: 9px;
+ width: 3em;
+ substraction: 0;
+ division: 1;
+}
+#operations .spacing {
+ height: 9px;
+ width: 3em;
+}
+.with-variables {
+ height: 16em;
+ width: 24em;
+ size: 1cm;
+}
+.with-functions {
+ color: #646464;
+ color: #ff8080;
+ color: #c94a4a;
+}
+.negative {
+ height: 0px;
+ width: 4px;
+}
+.shorthands {
+ padding: -1px 2px 0 -4px;
+}
+.rem-dimensions {
+ font-size: 5.5rem;
+}
+.colors {
+ color: #123;
+ border-color: #334455;
+ background-color: #000000;
+}
+.colors .other {
+ color: #222222;
+ border-color: #222222;
+}
+.negations {
+ variable: -4px;
+ variable1: 0px;
+ variable2: 0px;
+ variable3: 8px;
+ variable4: 0px;
+ paren: -4px;
+ paren2: 16px;
+}
--- /dev/null
+.parens {
+ border: 2px solid black;
+ margin: 1px 3px 16 3;
+ width: 36;
+ padding: 2px 36px;
+}
+.more-parens {
+ padding: 8 4 4 4px;
+ width-all: 96;
+ width-first: 16 * 6;
+ width-keep: (4 * 4) * 6;
+ height-keep: (7 * 7) + (8 * 8);
+ height-all: 113;
+ height-parts: 49 + 64;
+ margin-keep: (4 * (5 + 5) / 2) - (4 * 2);
+ margin-parts: 20 - 8;
+ margin-all: 12;
+ border-radius-keep: 4px * (1 + 1) / 4 + 3px;
+ border-radius-parts: 8px / 7px;
+ border-radius-all: 5px;
+}
+.negative {
+ neg-var: -1;
+ neg-var-paren: -(1);
+}
+.nested-parens {
+ width: 2 * (4 * (2 + (1 + 6))) - 1;
+ height: ((2 + 3) * (2 + 3) / (9 - 4)) + 1;
+}
+.mixed-units {
+ margin: 2px 4em 1 5pc;
+ padding: 6px 1em 2px 2;
+}
+.test-false-negatives {
+ a: (;
+}
--- /dev/null
+pi-test {
+ border: 0;
+ @not-variable: @not-variable;
+ ufo-width: 50%;
+ *-z-border: 1px dashed blue;
+ -www-border-top: 2px;
+ radius-is-not-a-border: true;
+ border-top-left-radius: 2em;
+ border-top-red-radius-: 3pt;
+ global-local-mixer-property: strong;
+}
+pi-test-merge {
+ pre-property-ish: high, middle, low, base;
+ pre-property-ish+: nice try dude;
+}
+pi-indirect-vars {
+ auto: auto;
+}
+pi-complex-values {
+ 3px rgba(255, 255, 0, 0.5), 3.141592653589793 /* foo */3px rgba(255, 255, 0, 0.5), 3.141592653589793 /* foo */: none;
+}
--- /dev/null
+#first > .one {
+ font-size: 2em;
+}
+#first > .one > #second .two > #deux {
+ width: 50%;
+}
+#first > .one > #second .two > #deux #third {
+ height: 100%;
+}
+#first > .one > #second .two > #deux #third:focus {
+ color: black;
+}
+#first > .one > #second .two > #deux #third:focus #fifth > #sixth .seventh #eighth + #ninth {
+ color: purple;
+}
+#first > .one > #second .two > #deux #fourth,
+#first > .one > #second .two > #deux #five,
+#first > .one > #second .two > #deux #six {
+ color: #110000;
+}
+#first > .one > #second .two > #deux #fourth .seven,
+#first > .one > #second .two > #deux #five .seven,
+#first > .one > #second .two > #deux #six .seven,
+#first > .one > #second .two > #deux #fourth .eight > #nine,
+#first > .one > #second .two > #deux #five .eight > #nine,
+#first > .one > #second .two > #deux #six .eight > #nine {
+ border: 1px solid black;
+}
+#first > .one > #second .two > #deux #fourth #ten,
+#first > .one > #second .two > #deux #five #ten,
+#first > .one > #second .two > #deux #six #ten {
+ color: red;
+}
--- /dev/null
+.tiny-scope {
+ color: #998899;
+}
+.scope1 {
+ color: blue;
+ border-color: black;
+}
+.scope1 .scope2 {
+ color: blue;
+}
+.scope1 .scope2 .scope3 {
+ color: red;
+ border-color: black;
+ background-color: white;
+}
+.scope {
+ scoped-val: green;
+}
+.heightIsSet {
+ height: 1024px;
+}
+.useHeightInMixinCall {
+ mixin-height: 1024px;
+}
+.imported {
+ exists: true;
+}
+.testImported {
+ exists: true;
+}
+#allAreUsedHere {
+ default: 'top level';
+ scope: 'top level';
+ sub-scope-only: 'inside';
+}
+#parentSelectorScope {
+ prop: white;
+}
--- /dev/null
+h1 a:hover,
+h2 a:hover,
+h3 a:hover,
+h1 p:hover,
+h2 p:hover,
+h3 p:hover {
+ color: red;
+}
+#all {
+ color: blue;
+}
+#the {
+ color: blue;
+}
+#same {
+ color: blue;
+}
+ul,
+li,
+div,
+q,
+blockquote,
+textarea {
+ margin: 0;
+}
+td {
+ margin: 0;
+ padding: 0;
+}
+td,
+input {
+ line-height: 1em;
+}
+a {
+ color: red;
+}
+a:hover {
+ color: blue;
+}
+div a {
+ color: green;
+}
+p a span {
+ color: yellow;
+}
+.foo .bar .qux,
+.foo .baz .qux {
+ display: block;
+}
+.qux .foo .bar,
+.qux .foo .baz {
+ display: inline;
+}
+.qux.foo .bar,
+.qux.foo .baz {
+ display: inline-block;
+}
+.qux .foo .bar .biz,
+.qux .foo .baz .biz {
+ display: none;
+}
+.a.b.c {
+ color: red;
+}
+.c .b.a {
+ color: red;
+}
+.foo .p.bar {
+ color: red;
+}
+.foo.p.bar {
+ color: red;
+}
+.foo + .foo {
+ background: amber;
+}
+.foo + .foo {
+ background: amber;
+}
+.foo + .foo,
+.foo + .bar,
+.bar + .foo,
+.bar + .bar {
+ background: amber;
+}
+.foo a > .foo a,
+.foo a > .bar a,
+.foo a > .foo b,
+.foo a > .bar b,
+.bar a > .foo a,
+.bar a > .bar a,
+.bar a > .foo b,
+.bar a > .bar b,
+.foo b > .foo a,
+.foo b > .bar a,
+.foo b > .foo b,
+.foo b > .bar b,
+.bar b > .foo a,
+.bar b > .bar a,
+.bar b > .foo b,
+.bar b > .bar b {
+ background: amber;
+}
+.other ::fnord {
+ color: red;
+}
+.other::fnord {
+ color: red;
+}
+.other ::bnord {
+ color: red;
+}
+.other::bnord {
+ color: red;
+}
+.blood {
+ color: red;
+}
+.bloodred {
+ color: green;
+}
+#blood.blood.red.black:blood {
+ color: black;
+}
+:nth-child(3) {
+ selector: interpolated;
+}
+.test:nth-child(3) {
+ selector: interpolated;
+}
+.test:nth-child(odd):not(:nth-child(3)) {
+ color: #ff0000;
+}
+[prop],
+[prop=10%],
+[prop="value3"],
+[prop*="val3"],
+[|prop~="val3"],
+[*|prop$="val3"],
+[ns|prop^="val3"],
+[3^="val3"],
+[3=3],
+[3] {
+ attributes: yes;
+}
+/*
+Large comment means chunk will be emitted after } which means chunk will begin with whitespace...
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+*/
+.blood {
+ color: red;
+}
+.foo:not(.tst.only-nested .level2:hover) {
+ test: only-nested;
+}
+.foo.nestend-and-non-nested:not(.tst.nestend-and-non-nested:hover) {
+ test: nestend-and-non-nested;
+}
+.selector:not(:hover) {
+ test: global scope;
+}
+.extend-this,
+.active.first-level .second-level,
+.first-level .second-level.active2 {
+ content: '\2661';
+}
--- /dev/null
+@import "css/background.css";
+@import "folder (1)/import-test-d.css";
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium), url(folder\ \(1\)/fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(folder\ \(1\)/images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(folder\ \(1\)/bg.jpg) no-repeat, url(folder\ \(1\)/bg.png) repeat-x top left, url(folder\ \(1\)/bg);
+}
+.values {
+ url: url('folder (1)/Trebuchet');
+}
+#logo {
+ width: 100px;
+ height: 100px;
+ background: url('assets/logo.png');
+ background: url("#inline-svg");
+}
+@font-face {
+ font-family: xecret;
+ src: url('assets/xecret.ttf');
+}
+#secret {
+ font-family: xecret, sans-serif;
+}
+#imported-relative-path {
+ background-image: url(../data/image.jpg);
+ border-image: url('../data/image.jpg');
+}
--- /dev/null
+.units {
+ cancels-to-nothing: 1;
+ cancels: 6px;
+}
--- /dev/null
+#strings {
+ background-image: url("http://son-of-a-banana.com");
+ quotes: "~" "~";
+ content: "#*%:&^,)!.(~*})";
+ empty: "";
+ brackets: "{" "}";
+ escapes: "\"hello\" \\world";
+ escapes2: "\"llo";
+}
+#comments {
+ content: "/* hello */ // not-so-secret";
+}
+#single-quote {
+ quotes: "'" "'";
+ content: '""#!&""';
+ empty: '';
+ semi-colon: ';';
+}
+#escaped {
+ filter: DX.Transform.MS.BS.filter(opacity=50);
+}
+#one-line {
+ image: url(http://tooks.com);
+}
+#crazy {
+ image: url(http://), "}", url("http://}");
+}
+#interpolation {
+ url: "http://lesscss.org/dev/image.jpg";
+ url2: "http://lesscss.org/image-256.jpg";
+ url3: "http://lesscss.org#445566";
+ url4: "http://lesscss.org/hello";
+ url5: "http://lesscss.org/54.4px";
+}
+.mix-mul-class {
+ color: blue;
+ color: red;
+ color: black;
+ color: orange;
+}
+.watermark {
+ family: Univers, Arial, Verdana, San-Serif;
+}
+#iterated-interpolation .mixin {
+ width: 100px;
+ weird: 100px;
+ width-str: "100px";
+ weird-str: "100px";
+}
+#iterated-interpolation .interpolation-mixin {
+ width: 100px;
+ weird: 100px;
+ width-str: "100px";
+ weird-str: "100px";
+}
--- /dev/null
+@font-face {
+ src: url("/fonts/garamond-pro.ttf?424242");
+ src: local(Futura-Medium), url(fonts.svg?424242#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html?424242") no-repeat 0 4px;
+ background: url("img.jpg?424242") center / 100px;
+ background: #ffffff url(image.png?424242) center / 1px 100px repeat-x scroll content-box padding-box;
+}
+#misc {
+ background-image: url(images/image.jpg?424242);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700&424242);
+ background-image: url("http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700&424242");
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(bg.jpg?424242) no-repeat, url(bg.png?424242) repeat-x top left, url(bg?424242);
+}
+.values {
+ url: url('Trebuchet?424242');
+}
+@font-face {
+ font-family: xecret;
+ src: url('../assets/xecret.ttf?424242');
+}
+#secret {
+ font-family: xecret, sans-serif;
+}
+#data-uri {
+ uri: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
+}
+#data-uri-guess {
+ uri: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
+}
+#data-uri-ascii {
+ uri-1: url("data:text/html,%3Ch1%3EThis%20page%20is%20100%25%20Awesome.%3C%2Fh1%3E%0A");
+ uri-2: url("data:text/html,%3Ch1%3EThis%20page%20is%20100%25%20Awesome.%3C%2Fh1%3E%0A");
+}
+#svg-functions {
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23000000%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23000000%22%2F%3E%3Cstop%20offset%3D%223%25%22%20stop-color%3D%22%23ffa500%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%221%25%22%20stop-color%3D%22%23c4c4c4%22%2F%3E%3Cstop%20offset%3D%223%25%22%20stop-color%3D%22%23ffa500%22%2F%3E%3Cstop%20offset%3D%225%25%22%20stop-color%3D%22%23008000%22%2F%3E%3Cstop%20offset%3D%2295%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+}
+#data-uri-with-spaces {
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(' data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==');
+}
--- /dev/null
+@import "css/background.css";
+@import "import/import-test-d.css";
+@import "file.css";
+.gray-gradient {
+ background: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220%22%2F%3E%3Cstop%20offset%3D%2260%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.05%22%2F%3E%3Cstop%20offset%3D%2270%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.1%22%2F%3E%3Cstop%20offset%3D%2273%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.15%22%2F%3E%3Cstop%20offset%3D%2275%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.2%22%2F%3E%3Cstop%20offset%3D%2280%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.25%22%2F%3E%3Cstop%20offset%3D%2285%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.3%22%2F%3E%3Cstop%20offset%3D%2288%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.35%22%2F%3E%3Cstop%20offset%3D%2290%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.4%22%2F%3E%3Cstop%20offset%3D%2295%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.45%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23999999%22%20stop-opacity%3D%220.5%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+}
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium), url(fonts.svg#MyGeometricModern) format("svg");
+ not-a-comment: url(//z);
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+ background: url("img.jpg") center / 100px;
+ background: #ffffff url(image.png) center / 1px 100px repeat-x scroll content-box padding-box;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+ background-image: url("http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700");
+}
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ url: url('Trebuchet');
+}
+#logo {
+ width: 100px;
+ height: 100px;
+ background: url('import/assets/logo.png');
+ background: url("#inline-svg");
+}
+@font-face {
+ font-family: xecret;
+ src: url('import/assets/xecret.ttf');
+}
+#secret {
+ font-family: xecret, sans-serif;
+}
+#imported-relative-path {
+ background-image: url(../data/image.jpg);
+ border-image: url('../data/image.jpg');
+}
+#relative-url-import {
+ background-image: url(../data/image.jpg);
+ border-image: url('../data/image.jpg');
+}
+#data-uri {
+ uri: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
+ background-image: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg=="), url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
+ uri-fragment: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==#fragment");
+}
+#data-uri-guess {
+ uri: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
+}
+#data-uri-ascii {
+ uri-1: url("data:text/html,%3Ch1%3EThis%20page%20is%20100%25%20Awesome.%3C%2Fh1%3E%0A");
+ uri-2: url("data:text/html,%3Ch1%3EThis%20page%20is%20100%25%20Awesome.%3C%2Fh1%3E%0A");
+}
+#file-functions {
+ uri: url('../data/data-uri-fail.png');
+ svg-not-base-64: url("data:image/svg+xml,%3Csvg%20height%3D%22100%22%20width%3D%22100%22%3E%0A%20%20%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20stroke%3D%22black%22%20stroke-width%3D%221%22%20fill%3D%22blue%22%20%2F%3E%0A%3C%2Fsvg%3E");
+ size: 640px 430px;
+ width: 640px;
+ height: 430px;
+}
+#svg-functions {
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23000000%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23000000%22%2F%3E%3Cstop%20offset%3D%223%25%22%20stop-color%3D%22%23ffa500%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+ background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%220%25%22%20y2%3D%22100%25%22%3E%3Cstop%20offset%3D%221%25%22%20stop-color%3D%22%23c4c4c4%22%2F%3E%3Cstop%20offset%3D%223%25%22%20stop-color%3D%22%23ffa500%22%2F%3E%3Cstop%20offset%3D%225%25%22%20stop-color%3D%22%23008000%22%2F%3E%3Cstop%20offset%3D%2295%25%22%20stop-color%3D%22%23ffffff%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E');
+}
+@font-face {
+ font-family: 'MyWebFont';
+ src: url(webfont.eot);
+ src: url('webfont.eot?#iefix') format('embedded-opentype'), url('webfont.woff') format('woff'), format('truetype') url('webfont.ttf'), url('webfont.svg#svgFontName') format('svg');
+}
--- /dev/null
+@charset "UTF-8";
+@namespace less "http://lesscss.org";
+@keyframes enlarger {
+ from {
+ font-size: 12px;
+ }
+ to {
+ font-size: 15px;
+ }
+}
+@-webkit-keyframes reducer {
+ from {
+ font-size: 13px;
+ }
+ to {
+ font-size: 10px;
+ }
+}
--- /dev/null
+.variables {
+ width: 14cm;
+}
+.variables {
+ height: 24px;
+ color: #888888;
+ font-family: "Trebuchet MS", Verdana, sans-serif;
+ quotes: "~" "~";
+}
+.redef {
+ zero: 0;
+}
+.redef .inition {
+ three: 3;
+}
+.values {
+ minus-one: -1;
+ font-family: 'Trebuchet', 'Trebuchet', 'Trebuchet';
+ color: #888888 !important;
+ same-color: #888888 !important;
+ same-again: #888888 !important;
+ multi-important: #888888 #888888, 'Trebuchet' !important;
+ multi: something 'A', B, C, 'Trebuchet';
+}
+.variable-names .quoted {
+ name: 'hello';
+}
+.variable-names .unquoted {
+ name: 'hello';
+}
+.variable-names .color-keyword {
+ name: 'hello';
+}
+.alpha {
+ filter: alpha(opacity=42);
+}
+.testPollution {
+ a: 'no-pollution';
+}
+.units {
+ width: 1px;
+ same-unit-as-previously: 1px;
+ square-pixel-divided: 1px;
+ odd-unit: 2;
+ percentage: 500%;
+ pixels: 500px;
+ conversion-metric-a: 30mm;
+ conversion-metric-b: 3cm;
+ conversion-imperial: 3in;
+ custom-unit: 420octocats;
+ custom-unit-cancelling: 18dogs;
+ mix-units: 2px;
+ invalid-units: 1px;
+}
+.units .fallback {
+ div-px-1: 10px;
+ div-px-2: 1px;
+ sub-px-1: 12.6px;
+ sub-cm-1: 9.666625cm;
+ mul-px-1: 19.6px;
+ mul-em-1: 19.6em;
+ mul-em-2: 196em;
+ mul-cm-1: 196cm;
+ add-px-1: 15.4px;
+ add-px-2: 393.35275591px;
+ mul-px-2: 140px;
+ mul-px-3: 140px;
+}
--- /dev/null
+.whitespace {
+ color: white;
+}
+.whitespace {
+ color: white;
+}
+.whitespace {
+ color: white;
+}
+.whitespace {
+ color: white;
+}
+.whitespace {
+ color: white ;
+}
+.white,
+.space,
+.mania {
+ color: white;
+}
+.no-semi-column {
+ color: white;
+}
+.no-semi-column {
+ color: white;
+ white-space: pre;
+}
+.no-semi-column {
+ border: 2px solid white;
+}
+.newlines {
+ background: the,
+ great,
+ wall;
+ border: 2px
+ solid
+ black;
+}
+.sel .newline_ws .tab_ws {
+ color: white;
+ background-position: 45 -23;
+}
--- /dev/null
+not actually a jpeg file
--- /dev/null
+<svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="black" stroke-width="1" fill="blue" />
+</svg>
\ No newline at end of file
--- /dev/null
+<h1>This page is 100% Awesome.</h1>
--- /dev/null
+var lessTest = require("./less-test"),
+ lessTester = lessTest(),
+ path = require("path"),
+ stylize = require('../lib/less-node/lessc-helper').stylize;
+
+function getErrorPathReplacementFunction(dir) {
+ return function(input, baseDir) {
+ return input.replace(/\{path\}/g, path.join(process.cwd(), baseDir, dir + "/"))
+ .replace(/\{node\}/g, "")
+ .replace(/\{\/node\}/g, "")
+ .replace(/\{pathrel\}/g, path.join(baseDir, dir + "/"))
+ .replace(/\{pathhref\}/g, "")
+ .replace(/\{404status\}/g, "")
+ .replace(/\r\n/g, '\n');
+ };
+}
+
+console.log("\n" + stylize("Less", 'underline') + "\n");
+lessTester.prepBomTest();
+lessTester.runTestSet({strictMath: true, relativeUrls: true, silent: true});
+lessTester.runTestSet({strictMath: true, strictUnits: true}, "errors/",
+ lessTester.testErrors, null, getErrorPathReplacementFunction("errors"));
+lessTester.runTestSet({strictMath: true, strictUnits: true, javascriptEnabled: false}, "no-js-errors/",
+ lessTester.testErrors, null, getErrorPathReplacementFunction("no-js-errors"));
+lessTester.runTestSet({strictMath: true, dumpLineNumbers: 'comments'}, "debug/", null,
+ function(name) { return name + '-comments'; });
+lessTester.runTestSet({strictMath: true, dumpLineNumbers: 'mediaquery'}, "debug/", null,
+ function(name) { return name + '-mediaquery'; });
+lessTester.runTestSet({strictMath: true, dumpLineNumbers: 'all'}, "debug/", null,
+ function(name) { return name + '-all'; });
+lessTester.runTestSet({strictMath: true, relativeUrls: false, rootpath: "folder (1)/"}, "static-urls/");
+lessTester.runTestSet({strictMath: true, compress: true}, "compression/");
+lessTester.runTestSet({strictMath: true, strictUnits: true}, "strict-units/");
+lessTester.runTestSet({}, "legacy/");
+lessTester.runTestSet({strictMath: true, strictUnits: true, sourceMap: true, globalVars: true }, "sourcemaps/",
+ lessTester.testSourcemap, null, null,
+ function(filename, type, baseFolder) {
+ if (type === "vars") {
+ return path.join(baseFolder, filename) + '.json';
+ }
+ return path.join('test/sourcemaps', filename) + '.json';
+ });
+lessTester.runTestSet({strictMath: true, strictUnits: true, sourceMap: {sourceMapFileInline: true}}, "sourcemaps-empty/", lessTester.testEmptySourcemap);
+lessTester.runTestSet({globalVars: true, banner: "/**\n * Test\n */\n"}, "globalVars/",
+ null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; });
+lessTester.runTestSet({modifyVars: true}, "modifyVars/",
+ null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; });
+lessTester.runTestSet({urlArgs: '424242'}, "url-args/");
+lessTester.runTestSet({paths: ['test/data/', 'test/less/import/']}, "include-path/");
+lessTester.testSyncronous({syncImport: true}, "import");
+lessTester.testSyncronous({syncImport: true}, "css");
+lessTester.testNoOptions();
+lessTester.finished();
--- /dev/null
+/*jshint latedef: nofunc */
+
+module.exports = function() {
+ var path = require('path'),
+ fs = require('fs'),
+ copyBom = require('./copy-bom')(),
+ doBomTest = false;
+
+ var less = require('../lib/less-node');
+ var stylize = require('../lib/less-node/lessc-helper').stylize;
+
+ var globals = Object.keys(global);
+
+ var oneTestOnly = process.argv[2],
+ isFinished = false;
+
+ var isVerbose = process.env.npm_config_loglevel === 'verbose';
+
+ var normalFolder = 'test/less';
+ var bomFolder = 'test/less-bom';
+
+ less.logger.addListener({
+ info: function(msg) {
+ if (isVerbose) {
+ process.stdout.write(msg + "\n");
+ }
+ },
+ warn: function(msg) {
+ process.stdout.write(msg + "\n");
+ },
+ error: function(msg) {
+ process.stdout.write(msg + "\n");
+ }
+ });
+
+ var queueList = [],
+ queueRunning = false;
+ function queue(func) {
+ if (queueRunning) {
+ //console.log("adding to queue");
+ queueList.push(func);
+ } else {
+ //console.log("first in queue - starting");
+ queueRunning = true;
+ func();
+ }
+ }
+ function release() {
+ if (queueList.length) {
+ //console.log("running next in queue");
+ var func = queueList.shift();
+ setTimeout(func, 0);
+ } else {
+ //console.log("stopping queue");
+ queueRunning = false;
+ }
+ }
+
+ var totalTests = 0,
+ failedTests = 0,
+ passedTests = 0;
+
+ less.functions.functionRegistry.addMultiple({
+ add: function (a, b) {
+ return new(less.tree.Dimension)(a.value + b.value);
+ },
+ increment: function (a) {
+ return new(less.tree.Dimension)(a.value + 1);
+ },
+ _color: function (str) {
+ if (str.value === "evil red") { return new(less.tree.Color)("600"); }
+ }
+ });
+
+ function testSourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
+ fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
+ process.stdout.write("- " + path.join(baseFolder, name) + ": ");
+ if (sourcemap === expectedSourcemap) {
+ ok('OK');
+ } else if (err) {
+ fail("ERROR: " + (err && err.message));
+ if (isVerbose) {
+ process.stdout.write("\n");
+ process.stdout.write(err.stack + "\n");
+ }
+ } else {
+ difference("FAIL", expectedSourcemap, sourcemap);
+ }
+ });
+ }
+
+ function testEmptySourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
+ process.stdout.write("- " + path.join(baseFolder, name) + ": ");
+ if (err) {
+ fail("ERROR: " + (err && err.message));
+ } else {
+ var expectedSourcemap = undefined;
+ if ( compiledLess !== "" ) {
+ difference("\nCompiledLess must be empty", "", compiledLess);
+
+ } else if (sourcemap !== expectedSourcemap) {
+ fail("Sourcemap must be undefined");
+ } else {
+ ok('OK');
+ }
+ }
+ }
+
+ function testErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
+ fs.readFile(path.join(baseFolder, name) + '.txt', 'utf8', function (e, expectedErr) {
+ process.stdout.write("- " + path.join(baseFolder, name) + ": ");
+ expectedErr = doReplacements(expectedErr, baseFolder);
+ if (!err) {
+ if (compiledLess) {
+ fail("No Error", 'red');
+ } else {
+ fail("No Error, No Output");
+ }
+ } else {
+ var errMessage = less.formatError(err);
+ if (errMessage === expectedErr) {
+ ok('OK');
+ } else {
+ difference("FAIL", expectedErr, errMessage);
+ }
+ }
+ });
+ }
+
+ function globalReplacements(input, directory) {
+ var p = path.join(process.cwd(), directory),
+ pathimport = path.join(process.cwd(), directory + "import/"),
+ pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); }),
+ pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); });
+
+ return input.replace(/\{path\}/g, p)
+ .replace(/\{pathesc\}/g, pathesc)
+ .replace(/\{pathimport\}/g, pathimport)
+ .replace(/\{pathimportesc\}/g, pathimportesc)
+ .replace(/\r\n/g, '\n');
+ }
+
+ function checkGlobalLeaks() {
+ return Object.keys(global).filter(function(v) {
+ return globals.indexOf(v) < 0;
+ });
+ }
+
+ function testSyncronous(options, filenameNoExtension) {
+ if (oneTestOnly && ("Test Sync " + filenameNoExtension) !== oneTestOnly) {
+ return;
+ }
+ totalTests++;
+ queue(function() {
+ var isSync = true;
+ toCSS(options, path.join(normalFolder, filenameNoExtension + ".less"), function (err, result) {
+ process.stdout.write("- Test Sync " + filenameNoExtension + ": ");
+
+ if (isSync) {
+ ok("OK");
+ } else {
+ fail("Not Sync");
+ }
+ release();
+ });
+ isSync = false;
+ });
+ }
+
+ function prepBomTest() {
+ copyBom.copyFolderWithBom(normalFolder, bomFolder);
+ doBomTest = true;
+ }
+
+ function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
+ var options2 = options ? JSON.parse(JSON.stringify(options)) : {};
+ runTestSetInternal(normalFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
+ if (doBomTest) {
+ runTestSetInternal(bomFolder, options2, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
+ }
+ }
+
+ function runTestSetNormalOnly(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
+ runTestSetInternal(normalFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
+ }
+
+ function runTestSetInternal(baseFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
+ foldername = foldername || "";
+
+ if (!doReplacements) {
+ doReplacements = globalReplacements;
+ }
+
+ function getBasename(file) {
+ return foldername + path.basename(file, '.less');
+ }
+
+ fs.readdirSync(path.join(baseFolder, foldername)).forEach(function (file) {
+ if (! /\.less/.test(file)) { return; }
+
+ var name = getBasename(file);
+
+ if (oneTestOnly && name !== oneTestOnly) {
+ return;
+ }
+
+ totalTests++;
+
+ if (options.sourceMap && !options.sourceMap.sourceMapFileInline) {
+ options.sourceMapOutputFilename = name + ".css";
+ options.sourceMapBasepath = path.join(process.cwd(), baseFolder);
+ options.sourceMapRootpath = "testweb/";
+ // TODO separate options?
+ options.sourceMap = options;
+ }
+
+ options.getVars = function(file) {
+ return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars', baseFolder), 'utf8'));
+ };
+
+ var doubleCallCheck = false;
+ queue(function() {
+ toCSS(options, path.join(baseFolder, foldername + file), function (err, result) {
+ if (doubleCallCheck) {
+ totalTests++;
+ fail("less is calling back twice");
+ process.stdout.write(doubleCallCheck + "\n");
+ process.stdout.write((new Error()).stack + "\n");
+ return;
+ }
+ doubleCallCheck = (new Error()).stack;
+
+ if (verifyFunction) {
+ var verificationResult = verifyFunction(name, err, result && result.css, doReplacements, result && result.map, baseFolder);
+ release();
+ return verificationResult;
+ }
+ if (err) {
+ fail("ERROR: " + (err && err.message));
+ if (isVerbose) {
+ process.stdout.write("\n");
+ process.stdout.write(err.stack + "\n");
+ }
+ release();
+ return;
+ }
+ var css_name = name;
+ if (nameModifier) { css_name = nameModifier(name); }
+ fs.readFile(path.join('test/css', css_name) + '.css', 'utf8', function (e, css) {
+ process.stdout.write("- " + path.join(baseFolder, css_name) + ": ");
+
+ css = css && doReplacements(css, path.join(baseFolder, foldername));
+ if (result.css === css) { ok('OK'); }
+ else {
+ difference("FAIL", css, result.css);
+ }
+ release();
+ });
+ });
+ });
+ });
+ }
+
+ function diff(left, right) {
+ require('diff').diffLines(left, right).forEach(function(item) {
+ if (item.added || item.removed) {
+ var text = item.value && item.value.replace("\n", String.fromCharCode(182) + "\n").replace('\ufeff', '[[BOM]]');
+ process.stdout.write(stylize(text, item.added ? 'green' : 'red'));
+ } else {
+ process.stdout.write(item.value && item.value.replace('\ufeff', '[[BOM]]'));
+ }
+ });
+ process.stdout.write("\n");
+ }
+
+ function fail(msg) {
+ process.stdout.write(stylize(msg, 'red') + "\n");
+ failedTests++;
+ endTest();
+ }
+
+ function difference(msg, left, right) {
+ process.stdout.write(stylize(msg, 'yellow') + "\n");
+ failedTests++;
+
+ diff(left, right);
+ endTest();
+ }
+
+ function ok(msg) {
+ process.stdout.write(stylize(msg, 'green') + "\n");
+ passedTests++;
+ endTest();
+ }
+
+ function finished() {
+ isFinished = true;
+ endTest();
+ }
+
+ function endTest() {
+ if (isFinished && ((failedTests + passedTests) >= totalTests)) {
+ var leaked = checkGlobalLeaks();
+
+ process.stdout.write("\n");
+ if (failedTests > 0) {
+ process.stdout.write(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed\n");
+ } else {
+ process.stdout.write(stylize("All Passed ", "green") + passedTests + " run\n");
+ }
+ if (leaked.length > 0) {
+ process.stdout.write("\n");
+ process.stdout.write(stylize("Global leak detected: ", "red") + leaked.join(', ') + "\n");
+ }
+
+ if (leaked.length || failedTests) {
+ process.on('exit', function() { process.reallyExit(1); });
+ }
+ }
+ }
+
+ function contains(fullArray, obj) {
+ for (var i = 0; i < fullArray.length; i++) {
+ if (fullArray[i] === obj) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function toCSS(options, path, callback) {
+ options = options || {};
+ var str = fs.readFileSync(path, 'utf8'), addPath = require('path').dirname(path);
+
+ options.paths = options.paths || [];
+ if (!contains(options.paths, addPath)) {
+ options.paths.push(addPath);
+ }
+ options.filename = require('path').resolve(process.cwd(), path);
+ options.optimization = options.optimization || 0;
+
+ if (options.globalVars) {
+ options.globalVars = options.getVars(path);
+ } else if (options.modifyVars) {
+ options.modifyVars = options.getVars(path);
+ }
+
+ less.render(str, options, callback);
+ }
+
+ function testNoOptions() {
+ if (oneTestOnly && "Integration" !== oneTestOnly) {
+ return;
+ }
+ totalTests++;
+ try {
+ process.stdout.write("- Integration - creating parser without options: ");
+ less.render("");
+ } catch(e) {
+ fail(stylize("FAIL\n", "red"));
+ return;
+ }
+ ok(stylize("OK\n", "green"));
+ }
+
+ return {
+ runTestSet: runTestSet,
+ runTestSetNormalOnly: runTestSetNormalOnly,
+ testSyncronous: testSyncronous,
+ testErrors: testErrors,
+ testSourcemap: testSourcemap,
+ testEmptySourcemap: testEmptySourcemap,
+ testNoOptions: testNoOptions,
+ prepBomTest: prepBomTest,
+ finished: finished
+ };
+};
--- /dev/null
+@charset "UTF-8";
+
+@import "import/import-charset-test";
\ No newline at end of file
--- /dev/null
+#yelow {
+ #short {
+ color: #fea;
+ }
+ #long {
+ color: #ffeeaa;
+ }
+ #rgba {
+ color: rgba(255, 238, 170, 0.1);
+ }
+ #argb {
+ color: argb(rgba(255, 238, 170, 0.1));
+ }
+}
+
+#blue {
+ #short {
+ color: #00f;
+ }
+ #long {
+ color: #0000ff;
+ }
+ #rgba {
+ color: rgba(0, 0, 255, 0.1);
+ }
+ #argb {
+ color: argb(rgba(0, 0, 255, 0.1));
+ }
+}
+
+#alpha #hsla {
+ color: hsla(11, 20%, 20%, 0.6);
+}
+
+#overflow {
+ .a { color: (#111111 - #444444); } // #000000
+ .b { color: (#eee + #fff); } // #ffffff
+ .c { color: (#aaa * 3); } // #ffffff
+ .d { color: (#00ee00 + #009900); } // #00ff00
+ .e { color: rgba(-99.9, 31.4159, 321, 0.42); }
+}
+
+#grey {
+ color: rgb(200, 200, 200);
+}
+
+#333333 {
+ color: rgb(20%, 20%, 20%);
+}
+
+#808080 {
+ color: hsl(50, 0%, 50%);
+}
+
+#00ff00 {
+ color: hsl(120, 100%, 50%);
+}
+
+.lightenblue {
+ color: lighten(blue, 10%);
+}
+
+.darkenblue {
+ color: darken(blue, 10%);
+}
+
+.unknowncolors {
+ color: blue2;
+ border: 2px solid superred;
+}
+
+.transparent {
+ color: transparent;
+ background-color: rgba(0, 0, 0, 0);
+}
+#alpha {
+ @colorvar: rgba(150, 200, 150, 0.7);
+ #fromvar {
+ opacity: alpha(@colorvar);
+ }
+ #short {
+ opacity: alpha(#aaa);
+ }
+ #long {
+ opacity: alpha(#bababa);
+ }
+ #rgba {
+ opacity: alpha(rgba(50, 120, 95, 0.2));
+ }
+ #hsl {
+ opacity: alpha(hsl(120, 100%, 50%));
+ }
+}
+
+#percentage {
+ color: red(rgb(100%, 0, 0));
+ border-color: rgba(100%, 0, 0, 50%);
+}
--- /dev/null
+/******************\
+* *
+* Comment Header *
+* *
+\******************/
+
+/*
+
+ Comment
+
+*/
+
+/*
+ * Comment Test
+ *
+ * - cloudhead (http://cloudhead.net)
+ *
+ */
+
+////////////////
+@var: "content";
+////////////////
+
+/* Colors
+ * ------
+ * #EDF8FC (background blue)
+ * #166C89 (darkest blue)
+ *
+ * Text:
+ * #333 (standard text) // A comment within a comment!
+ * #1F9EC9 (standard link)
+ *
+ */
+
+/* @group Variables
+------------------- */
+#comments /* boo *//* boo again*/,
+//.commented_out1
+//.commented_out2
+//.commented_out3
+.comments //end of comments1
+//end of comments2
+{
+ /**/ // An empty comment
+ color: red; /* A C-style comment */ /* A C-style comment */
+ background-color: orange; // A little comment
+ font-size: 12px;
+
+ /* lost comment */ content: @var;
+
+ border: 1px solid black;
+
+ // padding & margin //
+ padding: 0; // }{ '"
+ margin: 2em;
+} //
+
+/* commented out
+ #more-comments {
+ color: grey;
+ }
+*/
+
+.selector /* .with */, .lots, /* of */ .comments {
+ color/* survive */ /* me too */: grey, /* blue */ orange;
+ -webkit-border-radius: 2px /* webkit only */;
+ -moz-border-radius: (2px * 4) /* moz only with operation */;
+}
+
+.mixin_def_with_colors(@a: white, // in
+ @b: 1px //put in @b - causes problems! --->
+ ) // the
+ when (@a = white) {
+ .test {
+ color: @b;
+ }
+}
+.mixin_def_with_colors();
+
+// .s when
+//R/2
+
+.sr-only-focusable {
+ clip: auto;
+}
+
+@-webkit-keyframes /* Safari */ hover /* and Chrome */ {
+ 0% {
+ color: red;
+ }
+}
+
+#last { color: blue }
+//
+
+/* *//* { *//* *//* *//* */#div { color:#A33; }/* } */
+
+// line immediatly followed
+/*by block */
+@string_w_comment: ~"/* // Not commented out // */";
+#output-block { comment: @string_w_comment; }
+/*comment on last line*/
\ No newline at end of file
--- /dev/null
+@media all and/*! */(max-width:1024px) {}
+@-webkit-keyframes hover /* Safari and Chrome */{ }
+.bg {
+ background-image: linear-gradient(#333 /*{comment}*/, #111);
+}
+#planadvisor,
+/*comment*//*comment*/
+.first,/*comment*//*comment*/.planning {
+ margin:10px;
+ total-width: @total-width;
+}
+@base : 1;
+@column-width : @base * 6em; // Width of column */
+@gutter-width : 2em; // Width of column spacing */
+@columns : 12; // Number of Columns */
+@gridsystem-width : (@column-width * // For calculating the total */
+ @columns) + ( // width of the content area. */
+ @gutter-width * // We strongly recommend you */
+ @columns); // do not change this formula. */
+@total-width : @gridsystem-width; // set to 100% for fluid grid */
--- /dev/null
+#colours {
+ color1: #fea;
+ color2: #ffeeaa;
+ color3: rgba(255, 238, 170, 0.1);
+ @color1: #fea;
+ string: "@{color1}";
+ /* comments are stripped */
+ // both types!
+ /*! but not this type
+ Note preserved whitespace
+ */
+}
+dimensions {
+ val: 0.1px;
+ val: 0em;
+ val: 4cm;
+ val: 0.2;
+ val: 5;
+ angles-must-have-unit: 0deg;
+ durations-must-have-unit: 0s;
+ length-doesnt-have-unit: 0px;
+ width: auto\9;
+}
+@page {
+ marks: none;
+@top-left-corner {
+ vertical-align: top;
+}
+@top-left {
+ vertical-align: top;
+}
+}
+.shadow ^ .dom,
+body ^^ .shadow {
+ display: done;
+}
--- /dev/null
+.comma-delimited {
+ text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
+ -moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset,
+ 0pt 4px 6px rgba(255, 255, 255, 0.4) inset;
+ -webkit-transform: rotate(-0.0000000001deg);
+}
+@font-face {
+ font-family: Headline;
+ unicode-range: U+??????, U+0???, U+0-7F, U+A5;
+}
+.other {
+ -moz-transform: translate(0, 11em) rotate(-90deg);
+ transform: rotateX(45deg);
+}
+.item[data-cra_zy-attr1b-ut3=bold] {
+ font-weight: bold;
+}
+p:not([class*="lead"]) {
+ color: black;
+}
+
+input[type="text"].class#id[attr=32]:not(1) {
+ color: white;
+}
+
+div#id.class[a=1][b=2].class:not(1) {
+ color: white;
+}
+
+ul.comma > li:not(:only-child)::after {
+ color: white;
+}
+
+ol.comma > li:nth-last-child(2)::after {
+ color: white;
+}
+
+li:nth-child(4n+1),
+li:nth-child(-5n),
+li:nth-child(-n+2) {
+ color: white;
+}
+
+a[href^="http://"] {
+ color: black;
+}
+
+a[href$="http://"] {
+ color: black;
+}
+
+form[data-disabled] {
+ color: black;
+}
+
+p::before {
+ color: black;
+}
+
+#issue322 {
+ -webkit-animation: anim2 7s infinite ease-in-out;
+}
+
+@-webkit-keyframes frames {
+ 0% { border: 1px }
+ 5.5% { border: 2px }
+ 100% { border: 3px }
+}
+
+@keyframes fontbulger1 {
+ to {
+ font-size: 15px;
+ }
+ from,to {
+ font-size: 12px;
+ }
+ 0%,100% {
+ font-size: 12px;
+ }
+}
+
+.units {
+ font: 1.2rem/2rem;
+ font: 8vw/9vw;
+ font: 10vh/12vh;
+ font: 12vm/15vm;
+ font: 12vmin/15vmin;
+ font: 1.2ch/1.5ch;
+}
+
+@supports ( box-shadow: 2px 2px 2px black ) or
+ ( -moz-box-shadow: 2px 2px 2px black ) {
+ .outline {
+ box-shadow: 2px 2px 2px black;
+ -moz-box-shadow: 2px 2px 2px black;
+ }
+}
+
+@-x-document url-prefix(""github.com"") {
+ h1 {
+ color: red;
+ }
+}
+
+@viewport {
+ font-size: 10px;
+}
+@namespace foo url(http://www.example.com);
+
+foo|h1 { color: blue; }
+foo|* { color: yellow; }
+|h1 { color: red; }
+*|h1 { color: green; }
+h1 { color: green; }
+.upper-test {
+ UpperCaseProperties: allowed;
+}
+@host {
+ div {
+ display: block;
+ }
+}
+::distributed(input::placeholder) {
+ color: #b3b3b3;
+}
+.shadow ^ .dom,
+body ^^ .shadow {
+ display: done;
+}
+
+:host(.sel .a),
+:host-context(.sel .b),
+.sel /deep/ .b,
+::content .sel {
+ type: shadow-dom;
+}
+/deep/ b {
+ c: 'd';
+ &[e]{
+ f: 'g';
+ }
+}
+
+#issue2066 {
+ background: url('/images/icon-team.svg') 0 0 / contain;
+}
+@counter-style triangle {
+ system: cyclic;
+ symbols: ‣;
+ suffix: " ";
+}
+@-ms-viewport{
+//width: auto !important;
+}
\ No newline at end of file
--- /dev/null
+@ugly: fuchsia;
+
+.escape\|random\|char {
+ color: red;
+}
+
+.mixin\!tUp {
+ font-weight: bold;
+}
+
+// class="404"
+.\34 04 {
+ background: red;
+
+ strong {
+ color: @ugly;
+ .mixin\!tUp;
+ }
+}
+
+.trailingTest\+ {
+ color: red;
+}
+
+/* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */
+\62\6c\6f \63 \6B \0071 \000075o\74 e {
+ color: silver;
+}
+
+[ng\:cloak],
+ng\:form {
+ display: none;
+}
--- /dev/null
+
+.light when (lightness(@a) > 50%) {
+ color: green;
+}
+.dark when (lightness(@a) < 50%) {
+ color: orange;
+}
+@a: #ddd;
+
+.see-the {
+ @a: #444; // this mirrors what mixins do - they evaluate the guards at the point of definition
+ .light();
+ .dark();
+}
+
+.hide-the {
+ .light();
+ .dark();
+}
+
+.multiple-conditions-1 when (@b = 1), (@c = 2), (@d = 3) {
+ color: red;
+}
+
+.multiple-conditions-2 when (@b = 1), (@c = 2), (@d = 2) {
+ color: blue;
+}
+
+@b: 2;
+@c: 3;
+@d: 3;
+
+.inheritance when (@b = 2) {
+ .test {
+ color: black;
+ }
+ &:hover {
+ color: pink;
+ }
+ .hideme when (@b = 1) {
+ color: green;
+ }
+ & when (@b = 1) {
+ hideme: green;
+ }
+}
+
+.hideme when (@b = 1) {
+ .test {
+ color: black;
+ }
+ &:hover {
+ color: pink;
+ }
+ .hideme when (@b = 1) {
+ color: green;
+ }
+}
+
+& when (@b = 1) {
+ .hideme {
+ color: red;
+ }
+}
+
+.mixin-with-guard-inside(@colWidth) {
+ // selector with guard (applies also to & when() ...)
+ .clsWithGuard when (@colWidth <= 0) {
+ dispaly: none;
+ }
+}
+
+.mixin-with-guard-inside(0px);
+
+.dont-split-me-up {
+ width: 1px;
+ & when (@c = 3) {
+ color: red;
+ }
+ & when (@c = 3) {
+ height: 1px;
+ }
+ + & when (@c = 3) { // creates invalid css but tests that we don't fold it in
+ sibling: true;
+ }
+}
+
+.scope-check when (@c = 3) {
+ @k: 1px;
+ & when (@c = 3) {
+ @k: 2px;
+ sub-prop: @k;
+ }
+ prop: @k;
+}
+.scope-check-2 {
+ .scope-check();
+ @k:4px;
+}
+.errors-if-called when (@c = never) {
+ .mixin-doesnt-exist();
+}
+a:hover when (2 = true) {5:-}
\ No newline at end of file
--- /dev/null
+@charset "utf-8";
+div { color: black; }
+div { width: 99%; }
+
+* {
+ min-width: 45em;
+}
+
+h1, h2 > a > p, h3 {
+ color: none;
+}
+
+div.class {
+ color: blue;
+}
+
+div#id {
+ color: green;
+}
+
+.class#id {
+ color: purple;
+}
+
+.one.two.three {
+ color: grey;
+}
+
+@media print {
+ * {
+ font-size: 3em;
+ }
+}
+
+@media screen {
+ * {
+ font-size: 10px;
+ }
+}
+
+@font-face {
+ font-family: 'Garamond Pro';
+}
+
+a:hover, a:link {
+ color: #999;
+}
+
+p, p:first-child {
+ text-transform: none;
+}
+
+q:lang(no) {
+ quotes: none;
+}
+
+p + h1 {
+ font-size: +2.2em;
+}
+
+#shorthands {
+ border: 1px solid #000;
+ font: 12px/16px Arial;
+ font: 100%/16px Arial;
+ margin: 1px 0;
+ padding: 0 auto;
+}
+
+#more-shorthands {
+ margin: 0;
+ padding: 1px 0 2px 0;
+ font: normal small/20px 'Trebuchet MS', Verdana, sans-serif;
+ font: 0/0 a;
+ border-radius: 5px / 10px;
+}
+
+.misc {
+ -moz-border-radius: 2px;
+ display: -moz-inline-stack;
+ width: .1em;
+ background-color: #009998;
+ background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue));
+ margin: ;
+ .nested-multiple {
+ multiple-semi-colons: yes;;;;;;
+ };
+ filter: alpha(opacity=100);
+ width: auto\9;
+}
+
+#important {
+ color: red !important;
+ width: 100%!important;
+ height: 20px ! important;
+}
+
+.def-font(@name) {
+ @font-face {
+ font-family: @name
+ }
+}
+
+.def-font(font-a);
+.def-font(font-b);
+
+.æøå {
+ margin: 0;
+}
--- /dev/null
+@charset "ISO-8859-1";
+
+.mixin_import1() {
+ @media all {
+ .tst {
+ color: black;
+ @media screen {
+ color: red;
+ .tst3 {
+ color: white;
+ }
+ }
+ }
+ }
+}
+
+.mixin_import2() {
+ .tst2 {
+ color: white;
+ }
+}
+
+.tst3 {
+ color: grey;
+}
\ No newline at end of file
--- /dev/null
+@charset "UTF-8";
+
+@import "import/test.less";
+
+.start() {
+ .test2 {
+ color: red;
+ }
+}
+
+.mix() {
+ color: black;
+}
+
+.test1 {
+ .mix();
+}
+
+.start();
+
+.mixin_import1();
+
+.mixin_import2();
+
+@debug: 1;
+& when (@debug = 1) {
+ .test {
+ color: red;
+ & when (@debug = 1) {
+ width: 2;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+@ruleset: {
+ color: black;
+ background: white;
+ };
+
+@a: 1px;
+.wrap-mixin(@ruleset) {
+ @a: hidden and if you see this in the output its a bug;
+ @b: visible;
+ @d: magic-frame; // same behaviour as mixin calls - falls back to this frame
+ .wrap-selector {
+ @c: visible;
+ @ruleset();
+ visible-one: @b;
+ visible-two: @c;
+ }
+};
+
+.wrap-mixin({
+ color: black;
+ one: @a;
+ @b: hidden and if you see this in the output its a bug;
+ @c: hidden and if you see this in the output its a bug;
+ four: @d;
+});
+
+.wrap-mixin(@ruleset: {
+ color: red;
+});
+
+.wrap-mixin(@ruleset);
+
+.desktop-and-old-ie(@rules) {
+ @media screen and (min-width: 1200) { @rules(); }
+ html.lt-ie9 & { @rules(); }
+}
+
+header {
+ background: blue;
+
+ .desktop-and-old-ie({
+ background: red;
+ });
+}
+
+.wrap-mixin-calls-wrap(@ruleset) {
+ .wrap-mixin(@ruleset);
+};
+
+.wrap-mixin({
+ test: extra-wrap;
+ .wrap-mixin-calls-wrap({
+ test: wrapped-twice;
+ });
+});
+
+.wrap-mixin({
+ test-func: unit(90px);
+ test-arithmetic: unit((9+9), px);
+});
+// without mixins
+@ruleset-2: {
+ b: 1;
+};
+.without-mixins {
+ @ruleset-2();
+}
+@my-ruleset: {
+ .my-selector {
+ @media tv {
+ background-color: black;
+ }
+ }
+ };
+@media (orientation:portrait) {
+ @my-ruleset();
+ .wrap-media-mixin({
+ @media tv {
+ .triple-wrapped-mq {
+ triple: true;
+ }
+ }
+ });
+}
+.wrap-media-mixin(@ruleset) {
+ @media widescreen {
+ @media print {
+ @ruleset();
+ }
+ @ruleset();
+ }
+ @ruleset();
+}
+// unlocking mixins
+@my-mixins: {
+ .mixin() {
+ test: test;
+ }
+};
+@my-mixins();
+.a {
+ .mixin();
+}
\ No newline at end of file
--- /dev/null
+.a {
+ error: (1px + 3em);
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Incompatible units. Change the units or use the unit function. Bad units: 'px' and 'em'. in {path}add-mixed-units.less on line 2, column 3:
+1 .a {
+2 error: (1px + 3em);
+3 }
--- /dev/null
+.a {
+ error: ((1px * 2px) + (3em * 3px));
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Incompatible units. Change the units or use the unit function. Bad units: 'px*px' and 'em*px'. in {path}add-mixed-units2.less on line 2, column 3:
+1 .a {
+2 error: ((1px * 2px) + (3em * 3px));
+3 }
--- /dev/null
+
+@keyframes @name {
+ 50% {width: 20px;}
+}
--- /dev/null
+NameError: variable @name is undefined in {path}at-rules-undefined-var.less on line 2, column 12:\r
+1 \r
+2 @keyframes @name {\r
+3 50% {width: 20px;}\r
--- /dev/null
+@@demo: "hi";
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input in {path}bad-variable-declaration1.less on line 1, column 1:
+1 @@demo: "hi";
--- /dev/null
+.test {
+ color: color("NOT A COLOR");
+}
\ No newline at end of file
--- /dev/null
+ArgumentError: error evaluating function `color`: argument must be a color keyword or 3/6 digit hex e.g. #FFF in {path}color-func-invalid-color.less on line 2, column 10:
+1 .test {
+2 color: color("NOT A COLOR");
+3 }
--- /dev/null
+.a {
+ @wrongHEXColorCode: #DCALLB;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Invalid HEX color code in {path}color-invalid-hex-code.less on line 2, column 29:
+1 .a {
+2 @wrongHEXColorCode: #DCALLB;
+3 }
--- /dev/null
+.a {
+ @wrongHEXColorCode: #fffblack;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Invalid HEX color code in {path}color-invalid-hex-code2.less on line 2, column 29:
+1 .a {
+2 @wrongHEXColorCode: #fffblack;
+3 }
--- /dev/null
+
+selector when (default()) {
+ color: red;
+}
--- /dev/null
+SyntaxError: error evaluating function `default`: it is currently only allowed in parametric mixin guards, in {path}css-guard-default-func.less on line 2, column 16:\r
+1 \r
+2 selector when (default()) {\r
+3 color: red;\r
--- /dev/null
+@a: {
+ b: 1;
+};
+.a {
+ a: @a;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Rulesets cannot be evaluated on a property. in {path}detached-ruleset-1.less on line 5, column 3:
+4 .a {
+5 a: @a;
+6 }
--- /dev/null
+@a: {
+ b: 1;
+};
+.a {
+ a: @a();
+}
\ No newline at end of file
--- /dev/null
+ParseError: Expected ')' in {path}detached-ruleset-2.less on line 5, column 9:
+4 .a {
+5 a: @a();
+6 }
--- /dev/null
+@a: {
+ b: 1;
+};
+@a();
\ No newline at end of file
--- /dev/null
+SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}detached-ruleset-3.less on line 2, column 3:
+1 @a: {
+2 b: 1;
+3 };
--- /dev/null
+.mixin-definition(@a: {
+ b: 1;
+}) {
+ @a();
+}
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input in {path}detached-ruleset-4.less on line 3, column 4:
+2 b: 1;
+3 }) {
+4 @a();
--- /dev/null
+.mixin-definition(@b) {
+ @a();
+}
+.mixin-definition({color: red;});
\ No newline at end of file
--- /dev/null
+SyntaxError: variable @a is undefined in {path}detached-ruleset-5.less on line 4, column 1:
+3 }
+4 .mixin-definition({color: red;});
--- /dev/null
+.a {
+ b: {
+ color: red;
+ };
+}
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input in {path}detached-ruleset-6.less on line 2, column 6:
+1 .a {
+2 b: {
+3 color: red;
--- /dev/null
+.a {
+ error: (1px / 3em);
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Multiple units in dimension. Correct the units or use the unit function. Bad unit: px/em in {path}divide-mixed-units.less on line 2, column 3:
+1 .a {
+2 error: (1px / 3em);
+3 }
--- /dev/null
+:extend(.a all) {
+ property: red;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Extend must be used to extend a selector, it cannot be used on its own in {path}extend-no-selector.less on line 1, column 17:
+1 :extend(.a all) {
+2 property: red;
--- /dev/null
+.a:extend(.b all).c {
+ property: red;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Extend can only be used at the end of selector in {path}extend-not-at-end.less on line 1, column 21:
+1 .a:extend(.b all).c {
+2 property: red;
--- /dev/null
+@import malformed "this-statement-is-invalid.less";
--- /dev/null
+SyntaxError: malformed import statement in {path}import-malformed.less on line 1, column 1:
+1 @import malformed "this-statement-is-invalid.less";
+2
--- /dev/null
+.a {
+ color: green;
+ // tests line number for import reference is correct
+}
+
+@import "file-does-not-exist.less";
\ No newline at end of file
--- /dev/null
+FileError: '{pathhref}file-does-not-exist.less' wasn't found{404status}{node}. Tried - {path}file-does-not-exist.less,{pathrel}file-does-not-exist.less,file-does-not-exist.less{/node} in {path}import-missing.less on line 6, column 1:
+5
+6 @import "file-does-not-exist.less";
--- /dev/null
+@import "this-statement-is-invalid.less"
\ No newline at end of file
--- /dev/null
+SyntaxError: missing semi-colon or unrecognised media features on import in {path}import-no-semi.less on line 1, column 1:
+1 @import "this-statement-is-invalid.less"
--- /dev/null
+@import "imports/import-subfolder1.less";
\ No newline at end of file
--- /dev/null
+NameError: .mixin-not-defined is undefined in {path}mixin-not-defined.less on line 11, column 1:
+10
+11 .mixin-not-defined();
--- /dev/null
+@import "imports/import-subfolder2.less";
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input. Possibly missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1:
+3 }
+4 }
+5
--- /dev/null
+@import "subfolder/mixin-not-defined.less";
\ No newline at end of file
--- /dev/null
+@import "subfolder/parse-error-curly-bracket.less";
\ No newline at end of file
--- /dev/null
+.someclass
+{
+ font-weight: bold;
+}
\ No newline at end of file
--- /dev/null
+@import "../../mixin-not-defined.less";
\ No newline at end of file
--- /dev/null
+@import "../../parse-error-curly-bracket.less";
\ No newline at end of file
--- /dev/null
+.scope {
+ var: `this.foo.toJS`;
+}
--- /dev/null
+SyntaxError: JavaScript evaluation error: 'TypeError: Cannot read property 'toJS' of undefined' in {path}javascript-error.less on line 2, column 10:
+1 .scope {
+2 var: `this.foo.toJS`;
+3 }
--- /dev/null
+.scope {
+ @a: `@{b}`;
+}
--- /dev/null
+NameError: variable @b is undefined in {path}javascript-undefined-var.less on line 2, column 9:\r
+1 .scope {\r
+2 @a: `@{b}`;\r
+3 }\r
--- /dev/null
+.mixin(@a : 4, @b : 3, @c: 2) {
+ will: fail;
+}
+.mixin-test {
+ .mixin(@a: 5; @b: 6, @c: 7);
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Cannot mix ; and , as delimiter types in {path}mixed-mixin-definition-args-1.less on line 5, column 30:
+4 .mixin-test {
+5 .mixin(@a: 5; @b: 6, @c: 7);
+6 }
--- /dev/null
+.mixin(@a : 4, @b : 3, @c: 2) {
+ will: fail;
+}
+.mixin-test {
+ .mixin(@a: 5, @b: 6; @c: 7);
+}
--- /dev/null
+SyntaxError: Cannot mix ; and , as delimiter types in {path}mixed-mixin-definition-args-2.less on line 5, column 26:
+4 .mixin-test {
+5 .mixin(@a: 5, @b: 6; @c: 7);
+6 }
--- /dev/null
+
+.error-is-further-on() {
+}
+
+.pad-here-to-reproduce-error-in() {
+}
+
+.the-import-subfolder-test() {
+}
+
+.mixin-not-defined();
\ No newline at end of file
--- /dev/null
+NameError: .mixin-not-defined is undefined in {path}mixin-not-defined.less on line 11, column 1:
+10
+11 .mixin-not-defined();
--- /dev/null
+@saxofon:trumpete;
+
+.mixin(saxofon) {
+}
+
+.mixin(@saxofon);
\ No newline at end of file
--- /dev/null
+RuntimeError: No matching definition was found for `.mixin(trumpete)` in {path}mixin-not-matched.less on line 6, column 1:
+5
+6 .mixin(@saxofon);
--- /dev/null
+@saxofon:trumpete;
+
+.mixin(@a, @b) {
+}
+
+.mixin(@a: @saxofon);
\ No newline at end of file
--- /dev/null
+RuntimeError: No matching definition was found for `.mixin(@a:trumpete)` in {path}mixin-not-matched2.less on line 6, column 1:
+5
+6 .mixin(@a: @saxofon);
--- /dev/null
+.something {
+ & {
+ .a {value: a}
+ }
+
+ & {
+ .b {.a} // was Err. before 1.6.2
+ }
+}
\ No newline at end of file
--- /dev/null
+NameError: .a is undefined in {path}mixin-not-visible-in-scope-1.less on line 7, column 13:
+6 & {
+7 .b {.a} // was Err. before 1.6.2
+8 }
--- /dev/null
+
+guard-default-func-conflict {
+ .m(@x, 1) {}
+ .m(@x, 2) when (default()) {}
+ .m(@x, 2) when (default()) {}
+
+ .m(1, 1);
+ .m(1, 2);
+}
--- /dev/null
+RuntimeError: Ambiguous use of `default()` found when matching for `.m(1, 2)` in {path}mixins-guards-default-func-1.less on line 8, column 5:\r
+7 .m(1, 1);\r
+8 .m(1, 2);\r
+9 }\r
--- /dev/null
+
+guard-default-func-conflict {
+ .m(1) {}
+ .m(@x) when not(default()) {}
+ .m(@x) when (@x = 3) and (default()) {}
+
+ .m(2);
+ .m(3);
+}
--- /dev/null
+RuntimeError: Ambiguous use of `default()` found when matching for `.m(3)` in {path}mixins-guards-default-func-2.less on line 8, column 5:\r
+7 .m(2);\r
+8 .m(3);\r
+9 }\r
--- /dev/null
+
+guard-default-func-conflict {
+ .m(1) {}
+ .m(@x) when not(default()) {}
+ .m(@x) when not(default()) {}
+
+ .m(1);
+ .m(2);
+}
--- /dev/null
+RuntimeError: Ambiguous use of `default()` found when matching for `.m(2)` in {path}mixins-guards-default-func-3.less on line 8, column 5:\r
+7 .m(1);\r
+8 .m(2);\r
+9 }\r
--- /dev/null
+@ie8: true;
+.a when (@ie8 = true),
+.b {
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Guards are only currently allowed on a single selector. in {path}multiple-guards-on-css-selectors.less on line 3, column 1:
+2 .a when (@ie8 = true),
+3 .b {
+4 }
--- /dev/null
+@ie8: true;
+.a,
+.b when (@ie8 = true) {
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Guards are only currently allowed on a single selector. in {path}multiple-guards-on-css-selectors2.less on line 3, column 23:
+2 .a,
+3 .b when (@ie8 = true) {
+4 }
--- /dev/null
+/* Test */
+#blah {
+ // blah
+}
+.a {
+ error: (1px * 1em);
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Multiple units in dimension. Correct the units or use the unit function. Bad unit: em*px in {path}multiply-mixed-units.less on line 6, column 3:
+5 .a {
+6 error: (1px * 1em);
+7 }
--- /dev/null
+.a {
+ something: (12 (13 + 5 -23) + 5);
+}
\ No newline at end of file
--- /dev/null
+ParseError: Expected ')' in {path}parens-error-1.less on line 2, column 18:
+1 .a {
+2 something: (12 (13 + 5 -23) + 5);
+3 }
--- /dev/null
+.a {
+ something: (12 * (13 + 5 -23));
+}
\ No newline at end of file
--- /dev/null
+ParseError: Expected ')' in {path}parens-error-2.less on line 2, column 28:
+1 .a {
+2 something: (12 * (13 + 5 -23));
+3 }
--- /dev/null
+.a {
+ something: (12 + (13 + 10 -23));
+}
\ No newline at end of file
--- /dev/null
+ParseError: Expected ')' in {path}parens-error-3.less on line 2, column 29:
+1 .a {
+2 something: (12 + (13 + 10 -23));
+3 }
--- /dev/null
+body {
+ background-color: #fff;
+ }
+}
--- /dev/null
+ParseError: Unrecognised input. Possibly missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1:
+3 }
+4 }
+5
--- /dev/null
+@media (extra: bracket)) {
+ body {
+ background-color: #fff;
+ }
+}
--- /dev/null
+ParseError: Unrecognised input. Possibly missing opening '(' in {path}parse-error-extra-parens.less on line 1, column 24:
+1 @media (extra: bracket)) {
+2 body {
--- /dev/null
+body {
+ background-color: #fff;
--- /dev/null
+ParseError: Unrecognised input. Possibly missing something in {path}parse-error-missing-bracket.less on line 3, column 1:
+2 background-color: #fff;
+3
--- /dev/null
+@media (missing: bracket {
+ body {
+ background-color: #fff;
+ }
+}
--- /dev/null
+ParseError: Missing closing ')' in {path}parse-error-missing-parens.less on line 1, column 26:
+1 @media (missing: bracket {
+2 body {
--- /dev/null
+@import 'import/import-test.less';
+
+body
+{
+ font-family: arial, sans-serif;
+}
+
+nonsense;
+
+.clickable
+{
+ cursor: pointer;
+}
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input in {path}parse-error-with-import.less on line 8, column 9:
+7
+8 nonsense;
+9
--- /dev/null
+.a {
+ error: calc(1 %);
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: Invalid % without number in {path}percentage-missing-space.less on line 2, column 3:
+1 .a {
+2 error: calc(1 %);
+3 }
--- /dev/null
+a {
+ * : 1;
+}
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input in {path}property-asterisk-only-name.less on line 2, column 7:
+1 a {
+2 * : 1;
+3 }
--- /dev/null
+.test {
+ display/*/: block; /*sorry for IE5*/
+}
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input. Possibly missing opening '{' in {path}property-ie5-hack.less on line 3, column 1:
+2 display/*/: block; /*sorry for IE5*/
+3 }
--- /dev/null
+.a() {
+ prop:1;
+}
+.a();
\ No newline at end of file
--- /dev/null
+SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root.less on line 2, column 3:
+1 .a() {
+2 prop:1;
+3 }
--- /dev/null
+@import "property-in-root";
\ No newline at end of file
--- /dev/null
+SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root.less on line 2, column 3:
+1 .a() {
+2 prop:1;
+3 }
--- /dev/null
+prop:1;
+.a {
+ prop:1;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root3.less on line 1, column 1:
+1 prop:1;
+2 .a {
--- /dev/null
+a {outline-@{color}: green}
\ No newline at end of file
--- /dev/null
+NameError: variable @color is undefined in {path}property-interp-not-defined.less on line 1, column 12:\r
+1 a {outline-@{color}: green}\r
--- /dev/null
+@bodyColor: darken(@bodyColor, 30%);
\ No newline at end of file
--- /dev/null
+NameError: Recursive variable definition for @bodyColor in {path}recursive-variable.less on line 1, column 20:
+1 @bodyColor: darken(@bodyColor, 30%);
--- /dev/null
+x
\ No newline at end of file
--- /dev/null
+ParseError: Unrecognised input. Possibly missing something in {path}single-character.less on line 1, column 2:
+1 x
--- /dev/null
+.a {
+ a: svg-gradient(horizontal, black, white);
+}
\ No newline at end of file
--- /dev/null
+ArgumentError: error evaluating function `svg-gradient`: svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center' in {path}svg-gradient1.less on line 2, column 6:
+1 .a {
+2 a: svg-gradient(horizontal, black, white);
+3 }
--- /dev/null
+.a {
+ a: svg-gradient(to bottom, black, orange, 45%, white);
+}
\ No newline at end of file
--- /dev/null
+ArgumentError: error evaluating function `svg-gradient`: svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position] in {path}svg-gradient2.less on line 2, column 6:
+1 .a {
+2 a: svg-gradient(to bottom, black, orange, 45%, white);
+3 }
--- /dev/null
+.a {
+ a: svg-gradient(black, orange);
+}
\ No newline at end of file
--- /dev/null
+ArgumentError: error evaluating function `svg-gradient`: svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position] in {path}svg-gradient3.less on line 2, column 6:
+1 .a {
+2 a: svg-gradient(black, orange);
+3 }
--- /dev/null
+.a {
+ font-size: unit(80/16,rem);
+}
\ No newline at end of file
--- /dev/null
+ArgumentError: error evaluating function `unit`: the first argument to unit must be a number. Have you forgotten parenthesis? in {path}unit-function.less on line 2, column 14:
+1 .a {
+2 font-size: unit(80/16,rem);
+3 }
--- /dev/null
+//very simple chaining
+.a {
+ color: black;
+}
+.b:extend(.a) {}
+.c:extend(.b) {}
+
+//very simple chaining, ordering not important
+
+.d:extend(.e) {}
+.e:extend(.f) {}
+.f {
+ color: black;
+}
+
+//extend with all
+
+.g.h {
+ color: black;
+}
+.i.j:extend(.g all) {
+ color: white;
+}
+.k:extend(.i all) {}
+
+//extend multi-chaining
+
+.l {
+ color: black;
+}
+.m:extend(.l){}
+.n:extend(.m){}
+.o:extend(.n){}
+.p:extend(.o){}
+.q:extend(.p){}
+.r:extend(.q){}
+.s:extend(.r){}
+.t:extend(.s){}
+
+// self referencing is ignored
+
+.u {color: black;}
+.v.u.v:extend(.u all){}
+
+// circular reference because the new extend product will match the existing extend
+
+.w:extend(.w) {color: black;}
+.v.w.v:extend(.w all){}
+
+// classic circular references
+
+.x:extend(.z) {
+ color: x;
+}
+.y:extend(.x) {
+ color: y;
+}
+.z:extend(.y) {
+ color: z;
+}
+
+//very simple chaining, but with the extend inside the ruleset
+.va {
+ color: black;
+}
+.vb {
+ &:extend(.va);
+ color: white;
+}
+.vc {
+ &:extend(.vb);
+}
+
+// media queries - dont extend outside, do extend inside
+
+@media tv {
+ .ma:extend(.a,.b,.c,.d,.e,.f,.g,.h,.i,.j,.k,.l,.m,.n,.o,.p,.q,.r,.s,.t,.u,.v,.w,.x,.y,.z,.md) {
+ color: black;
+ }
+ .md {
+ color: white;
+ }
+ @media plasma {
+ .me, .mf {
+ &:extend(.mb,.md);
+ background: red;
+ }
+ }
+}
+.mb:extend(.ma) {};
+.mc:extend(.mb) {};
\ No newline at end of file
--- /dev/null
+.clearfix {
+ *zoom: 1;
+ &:after {
+ content: '';
+ display: block;
+ clear: both;
+ height: 0;
+ }
+}
+
+.foo {
+ &:extend(.clearfix all);
+ color: red;
+}
+
+.bar {
+ &:extend(.clearfix all);
+ color: blue;
+}
--- /dev/null
+.replace.replace,
+.c.replace + .replace {
+ .replace,
+ .c {
+ prop: copy-paste-replace;
+ }
+}
+.rep_ace:extend(.replace.replace .replace) {}
+
+.a .b .c {
+ prop: not_effected;
+}
+
+.a {
+ prop: is_effected;
+ .b {
+ prop: not_effected;
+ }
+ .b.c {
+ prop: not_effected;
+ }
+}
+
+.c, .a {
+ .b, .a {
+ .a, .c {
+ prop: not_effected;
+ }
+ }
+}
+
+.effected {
+ &:extend(.a);
+ &:extend(.b);
+ &:extend(.c);
+}
+
+.e {
+ && {
+ prop: extend-double;
+ &:hover {
+ hover: not-extended;
+ }
+ }
+}
+.dbl:extend(.e.e) {}
--- /dev/null
+.ext1 .ext2 {
+ background: black;
+}
+
+@media tv {
+ .ext1 .ext3 {
+ color: white;
+ }
+ .tv-lowres :extend(.ext1 all) {
+ background: blue;
+ }
+ @media hires {
+ .ext1 .ext4 {
+ color: green;
+ }
+ .tv-hires :extend(.ext1 all) {
+ background: red;
+ }
+ }
+}
+
+.all:extend(.ext1 all) {
+
+}
\ No newline at end of file
--- /dev/null
+.sidebar {
+ width: 300px;
+ background: red;
+
+ .box {
+ background: #FFF;
+ border: 1px solid #000;
+ margin: 10px 0;
+ }
+}
+
+.sidebar2 {
+ &:extend(.sidebar all);
+ background: blue;
+}
+
+.type1 {
+ .sidebar3 {
+ &:extend(.sidebar all);
+ background: green;
+ }
+}
+
+.type2 {
+ &.sidebar4 {
+ &:extend(.sidebar all);
+ background: red;
+ }
+}
+
+.button {
+ color: black;
+ &:hover {
+ color: white;
+ }
+}
+.submit {
+ &:extend(.button);
+ &:hover:extend(.button:hover) {}
+}
+
+.nomatch {
+ &:hover:extend(.button :hover) {}
+}
+
+.button2 {
+ :hover {
+ nested: white;
+ }
+}
+.button2 :hover {
+ notnested: black;
+}
+
+.nomatch :extend(.button2:hover) {}
+
+.amp-test-a,
+.amp-test-b {
+ .amp-test-c &.amp-test-d&.amp-test-e {
+ .amp-test-f&+&.amp-test-g:extend(.amp-test-h) {}
+ }
+}
+.amp-test-h {
+ test: extended by masses of selectors;
+}
\ No newline at end of file
--- /dev/null
+.error {
+ border: 1px #f00;
+ background: #fdd;
+}
+.error.intrusion {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+.intrusion .error {
+ display: none;
+}
+.badError:extend(.error all) {
+ border-width: 3px;
+}
+
+.foo .bar, .foo .baz {
+ display: none;
+}
+
+.ext1 .ext2
+ :extend(.foo all) {
+}
+
+.ext3:extend(.foo all),
+.ext4:extend(.foo all) {
+}
+
+div.ext5,
+.ext6 > .ext5 {
+ width: 100px;
+}
+
+.should-not-exist-in-output,
+.ext7:extend(.ext5 all) {
+}
+
+.ext {
+ test: 1;
+}
+// same as
+// .a .c:extend(.ext all)
+// .b .c:extend(.ext all)
+// .a .c .d
+// .b .c .d
+.a, .b {
+ test: 2;
+ .c:extend(.ext all) {
+ test: 3;
+ .d {
+ test: 4;
+ }
+ }
+}
+
+.replace.replace,
+.c.replace + .replace {
+ .replace,
+ .c {
+ prop: copy-paste-replace;
+ }
+}
+.rep_ace:extend(.replace all) {}
+
+.attributes {
+ [data="test"] {
+ extend: attributes;
+ }
+ .attribute-test {
+ &:extend([data="test"] all);
+ }
+ [data] {
+ extend: attributes2;
+ }
+ .attribute-test2 {
+ &:extend([data] all); //you could argue it should match [data="test"]... not for now though...
+ }
+ @attr-data: "test3";
+ [data=@{attr-data}] {
+ extend: attributes2;
+ }
+ .attribute-test {
+ &:extend([data="test3"] all);
+ }
+}
+
+.header {
+ .header-nav {
+ background: red;
+ &:before {
+ background: blue;
+ }
+ }
+}
+
+.footer {
+ .footer-nav {
+ &:extend( .header .header-nav all );
+ }
+}
\ No newline at end of file
--- /dev/null
+.error {
+ border: 1px #f00;
+ background: #fdd;
+}
+.error.intrusion {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+.intrusion .error {
+ display: none;
+}
+.badError {
+ &:extend(.error all);
+ border-width: 3px;
+}
+
+.foo .bar, .foo .baz {
+ display: none;
+}
+
+.ext1 .ext2 {
+ &:extend(.foo all);
+}
+
+.ext3,
+.ext4 {
+ &:extend(.foo all);
+ &:extend(.bar all);
+}
+
+div.ext5,
+.ext6 > .ext5 {
+ width: 100px;
+}
+
+.ext7 {
+ &:extend(.ext5 all);
+}
+
+.ext8.ext9 {
+ result: add-foo;
+}
+.ext8 .ext9,
+.ext8 + .ext9,
+.ext8 > .ext9 {
+ result: bar-matched;
+}
+.ext8.nomatch {
+ result: none;
+}
+.ext8 {
+ .ext9 {
+ result: match-nested-bar;
+ }
+}
+.ext8 {
+ &.ext9 {
+ result: match-nested-foo;
+ }
+}
+
+.fuu:extend(.ext8.ext9 all) {}
+.buu:extend(.ext8 .ext9 all) {}
+.zap:extend(.ext8 + .ext9 all) {}
+.zoo:extend(.ext8 > .ext9 all) {}
+
+.aa {
+ color: black;
+ .dd {
+ background: red;
+ }
+}
+.bb {
+ background: red;
+ .bb {
+ color: black;
+ }
+}
+.cc:extend(.aa,.bb) {}
+.ee:extend(.dd all,.bb) {}
+.ff:extend(.dd,.bb all) {}
\ No newline at end of file
--- /dev/null
+
+// simple array/list:
+
+.multiunit {
+ @v: abc "abc" 1 1px 1% #123;
+ length: length(@v);
+ extract: extract(@v, 1) extract(@v, 2) extract(@v, 3) extract(@v, 4) extract(@v, 5) extract(@v, 6);
+}
+
+.incorrect-index {
+ @v1: a b c;
+ @v2: a, b, c;
+ v1: extract(@v1, 5);
+ v2: extract(@v2, -2);
+}
+
+.scalar {
+ @var: variable;
+ var-value: extract(@var, 1);
+ var-length: length(@var);
+ ill-index: extract(@var, 2);
+
+ name-value: extract(name, 1);
+ string-value: extract("string", 1);
+ number-value: extract(12345678, 1);
+ color-value: extract(blue, 1);
+ rgba-value: extract(rgba(80, 160, 240, 0.67), 1);
+ empty-value: extract(~'', 1);
+
+ name-length: length(name);
+ string-length: length("string");
+ number-length: length(12345678);
+ color-length: length(blue);
+ rgba-length: length(rgba(80, 160, 240, 0.67));
+ empty-length: length(~'');
+}
+
+.mixin-arguments {
+ .mixin-args(a b c d);
+ .mixin-args(a, b, c, d);
+ .mixin-args(1; 2; 3; 4);
+}
+
+.mixin-args(@value) {
+ &-1 {
+ length: length(@value);
+ extract: extract(@value, 3) ~"|" extract(@value, 2) ~"|" extract(@value, 1);
+ }
+}
+
+.mixin-args(...) {
+ &-2 {
+ length: length(@arguments);
+ extract: extract(@arguments, 3) ~"|" extract(@arguments, 2) ~"|" extract(@arguments, 1);
+ }
+}
+
+.mixin-args(@values...) {
+ &-3 {
+ length: length(@values);
+ extract: extract(@values, 3) ~"|" extract(@values, 2) ~"|" extract(@values, 1);
+ }
+}
+
+.mixin-args(@head, @tail...) {
+ &-4 {
+ length: length(@tail);
+ extract: extract(@tail, 2) ~"|" extract(@tail, 1);
+ }
+}
+
+// "multidimensional" array/list
+
+.md-space-comma {
+ @v: a b c, 1 2 3, "x" "y" "z";
+ length-1: length(@v);
+ extract-1: extract(@v, 2);
+ length-2: length(extract(@v, 2));
+ extract-2: extract(extract(@v, 2), 2);
+
+ &-as-args {.mixin-args(a b c, 1 2 3, "x" "y" "z")}
+}
+
+.md-cat-space-comma {
+ @a: a b c;
+ @b: 1 2 3;
+ @c: "x" "y" "z";
+ @v: @a, @b, @c;
+ length-1: length(@v);
+ extract-1: extract(@v, 2);
+ length-2: length(extract(@v, 2));
+ extract-2: extract(extract(@v, 2), 2);
+
+ &-as-args {.mixin-args(@a, @b, @c)}
+}
+
+.md-cat-comma-space {
+ @a: a, b, c;
+ @b: 1, 2, 3;
+ @c: "x", "y", "z";
+ @v: @a @b @c;
+ length-1: length(@v);
+ extract-1: extract(@v, 2);
+ length-2: length(extract(@v, 2));
+ extract-2: extract(extract(@v, 2), 2);
+
+ &-as-args {.mixin-args(@a @b @c)}
+}
+
+.md-3D {
+ @a: a b c d, 1 2 3 4;
+ @b: 5 6 7 8, e f g h;
+ .3D(@a, @b);
+
+ .3D(...) {
+
+ @v1: @arguments;
+ length-1: length(@v1);
+ extract-1: extract(@v1, 1);
+
+ @v2: extract(@v1, 2);
+ length-2: length(@v2);
+ extract-2: extract(@v2, 1);
+
+ @v3: extract(@v2, 1);
+ length-3: length(@v3);
+ extract-3: extract(@v3, 3);
+
+ @v4: extract(@v3, 4);
+ length-4: length(@v4);
+ extract-4: extract(@v4, 1);
+ }
+}
--- /dev/null
+#functions {
+ @var: 10;
+ @colors: #000, #fff;
+ color: _color("evil red"); // #660000
+ width: increment(15);
+ height: undefined("self");
+ border-width: add(2, 3);
+ variable: increment(@var);
+ background: linear-gradient(@colors);
+}
+
+#built-in {
+ @r: 32;
+ escaped: e("-Some::weird(#thing, y)");
+ lighten: lighten(#ff0000, 40%);
+ darken: darken(#ff0000, 40%);
+ saturate: saturate(#29332f, 20%);
+ desaturate: desaturate(#203c31, 20%);
+ greyscale: greyscale(#203c31);
+ hsl-clamp: hsl(380, 150%, 150%);
+ spin-p: spin(hsl(340, 50%, 50%), 40);
+ spin-n: spin(hsl(30, 50%, 50%), -40);
+ luma-white: luma(#fff);
+ luma-black: luma(#000);
+ luma-black-alpha: luma(rgba(0,0,0,0.5));
+ luma-red: luma(#ff0000);
+ luma-green: luma(#00ff00);
+ luma-blue: luma(#0000ff);
+ luma-yellow: luma(#ffff00);
+ luma-cyan: luma(#00ffff);
+ luma-differs-from-luminance: luma(#ff3600);
+ luminance-white: luma(#fff);
+ luminance-black: luma(#000);
+ luminance-black-alpha: luma(rgba(0,0,0,0.5));
+ luminance-red: luma(#ff0000);
+ luminance-differs-from-luma: luminance(#ff3600);
+ contrast-filter: contrast(30%);
+ saturate-filter: saturate(5%);
+ contrast-white: contrast(#fff);
+ contrast-black: contrast(#000);
+ contrast-red: contrast(#ff0000);
+ contrast-green: contrast(#00ff00);
+ contrast-blue: contrast(#0000ff);
+ contrast-yellow: contrast(#ffff00);
+ contrast-cyan: contrast(#00ffff);
+ contrast-light: contrast(#fff, #111111, #eeeeee);
+ contrast-dark: contrast(#000, #111111, #eeeeee);
+ contrast-wrongorder: contrast(#fff, #eeeeee, #111111, 0.5);
+ contrast-light-thresh: contrast(#fff, #111111, #eeeeee, 0.5);
+ contrast-dark-thresh: contrast(#000, #111111, #eeeeee, 0.5);
+ contrast-high-thresh: contrast(#555, #111111, #eeeeee, 0.6);
+ contrast-low-thresh: contrast(#555, #111111, #eeeeee, 0.09);
+ contrast-light-thresh-per: contrast(#fff, #111111, #eeeeee, 50%);
+ contrast-dark-thresh-per: contrast(#000, #111111, #eeeeee, 50%);
+ contrast-high-thresh-per: contrast(#555, #111111, #eeeeee, 60%);
+ contrast-low-thresh-per: contrast(#555, #111111, #eeeeee, 9%);
+ replace: replace("Hello, Mars.", "Mars\.", "World!");
+ replace-captured: replace("This is a string.", "(string)\.$", "new $1.");
+ replace-with-flags: replace("One + one = 4", "one", "2", "gi");
+ replace-single-quoted: replace('foo-1', "1", "2");
+ replace-escaped-string: replace(~"bar-1", "1", "2");
+ replace-keyword: replace(baz-1, "1", "2");
+ format: %("rgb(%d, %d, %d)", @r, 128, 64);
+ format-string: %("hello %s", "world");
+ format-multiple: %("hello %s %d", "earth", 2);
+ format-url-encode: %("red is %A", #ff0000);
+ format-single-quoted: %('hello %s', "single world");
+ format-escaped-string: %(~"hello %s", "escaped world");
+ eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64));
+
+ unitless: unit(12px);
+ unit: unit((13px + 1px), em);
+ unitpercentage: unit(100, %);
+
+ get-unit: get-unit(10px);
+ get-unit-empty: get-unit(10);
+
+ hue: hue(hsl(98, 12%, 95%));
+ saturation: saturation(hsl(98, 12%, 95%));
+ lightness: lightness(hsl(98, 12%, 95%));
+ hsvhue: hsvhue(hsv(98, 12%, 95%));
+ hsvsaturation: hsvsaturation(hsv(98, 12%, 95%));
+ hsvvalue: hsvvalue(hsv(98, 12%, 95%));
+ red: red(#f00);
+ green: green(#0f0);
+ blue: blue(#00f);
+ rounded: round((@r/3));
+ rounded-two: round((@r/3), 2);
+ roundedpx: round((10px / 3));
+ roundedpx-three: round((10px / 3), 3);
+ rounded-percentage: round(10.2%);
+ ceil: ceil(10.1px);
+ floor: floor(12.9px);
+ sqrt: sqrt(25px);
+ pi: pi();
+ mod: mod(13m, 11cm); // could take into account units, doesn't at the moment
+ abs: abs(-4%);
+ tan: tan(42deg);
+ sin: sin(10deg);
+ cos: cos(12);
+ atan: atan(tan(0.1rad));
+ atan: convert(acos(cos(34deg)), deg);
+ atan: convert(acos(cos(50grad)), deg);
+ pow: pow(8px, 2);
+ pow: pow(4, 3);
+ pow: pow(3, 3em);
+ min: min(0);
+ min: min(6, 5);
+ min: min(1pt, 3pt);
+ min: min(1cm, 3mm);
+ max: max(1, 3);
+ max: max(3em, 1em, 2em, 5em);
+ percentage: percentage((10px / 50));
+ color-quoted-digit: color("#dda0dd");
+ color-quoted-keyword: color("plum");
+ color-color: color(#dda0dd);
+ color-keyword: color(plum);
+ tint: tint(#777777, 13);
+ tint-full: tint(#777777, 100);
+ tint-percent: tint(#777777, 13%);
+ tint-negative: tint(#777777, -13%);
+ shade: shade(#777777, 13);
+ shade-full: shade(#777777, 100);
+ shade-percent: shade(#777777, 13%);
+ shade-negative: shade(#777777, -13%);
+
+ fade-out: fadeOut(red, 5%); // support fadeOut and fadeout
+ fade-in: fadein(fadeout(red, 10%), 5%);
+
+ hsv: hsv(5, 50%, 30%);
+ hsva: hsva(3, 50%, 30%, 0.2);
+
+ mix: mix(#ff0000, #ffff00, 80);
+ mix-0: mix(#ff0000, #ffff00, 0);
+ mix-100: mix(#ff0000, #ffff00, 100);
+ mix-weightless: mix(#ff0000, #ffff00);
+ mixt: mix(#ff0000, transparent);
+
+ .is-a {
+ @rules: {
+ color: red;
+ };
+ ruleset: isruleset(@rules);
+ color: iscolor(#ddd);
+ color1: iscolor(red);
+ color2: iscolor(rgb(0, 0, 0));
+ color3: iscolor(transparent);
+ keyword: iskeyword(hello);
+ number: isnumber(32);
+ string: isstring("hello");
+ pixel: ispixel(32px);
+ percent: ispercentage(32%);
+ em: isem(32em);
+ cat: isunit(32cat, cat);
+ no-unit-is-empty: isunit(32, '');
+ case-insensitive-1: isunit(32CAT, cat);
+ case-insensitive-2: isunit(32px, PX);
+ }
+}
+
+#alpha {
+ alpha: darken(hsla(25, 50%, 50%, 0.6), 10%);
+ alpha2: alpha(rgba(3, 4, 5, 0.5));
+ alpha3: alpha(transparent);
+}
+
+#blendmodes {
+ multiply: multiply(#f60000, #f60000);
+ screen: screen(#f60000, #0000f6);
+ overlay: overlay(#f60000, #0000f6);
+ softlight: softlight(#f60000, #ffffff);
+ hardlight: hardlight(#f60000, #0000f6);
+ difference: difference(#f60000, #0000f6);
+ exclusion: exclusion(#f60000, #0000f6);
+ average: average(#f60000, #0000f6);
+ negation: negation(#f60000, #313131);
+}
+
+#extract-and-length {
+ @anon: A B C 1 2 3;
+ extract: extract(@anon, 6) extract(@anon, 5) extract(@anon, 4) extract(@anon, 3) extract(@anon, 2) extract(@anon, 1);
+ length: length(@anon);
+}
+
+#quoted-functions-in-mixin {
+ // Quoted type may have some weird side-effects when used in mixins (#2308)
+ .mixin();
+ .mixin() {
+ replace-double-quoted: replace('foo-1', "1", "2");
+ replace-single-quoted: replace('foo-3', "3", "4");
+ replace-escaped-string: replace(~"bar-1", "1", "2");
+ replace-keyword: replace(baz-1, "1", "2");
+ replace-anonymous: replace(e("qux-1"), "1", "2");
+ format-double-quoted: %("hello %s", "world");
+ format-single-quoted: %('hello %s', "single world");
+ format-escaped-string: %(~"hello %s", "escaped world");
+ format-keyword: %(hello);
+ format-anonymous: %(e("hello %s"), "anonymous world");
+ }
+}
+
+#list-details {
+ @list:
+ a 1, // Some comment
+ b 2;
+
+ length: length(@list);
+ one: extract(@list, 1);
+ @two: extract(@list, 2);
+ two: @two;
+ two-length: length(@two);
+ two-one: extract(@two, 1);
+ two-two: extract(@two, 2);
+}
--- /dev/null
+{
+ "the-border": "1px",
+ "base-color": "#111",
+ "red": "#842210"
+}
\ No newline at end of file
--- /dev/null
+#header {
+ color: (@base-color * 3);
+ border-left: @the-border;
+ border-right: (@the-border * 2);
+}
+#footer {
+ color: (@base-color + #003300);
+ border-color: @red;
+}
+@red: desaturate(red, 10%); // less file overrides passed in color <- note line comment on last line to check it is okay
\ No newline at end of file
--- /dev/null
+{
+ "my-color": "red"
+}
\ No newline at end of file
--- /dev/null
+.class {
+ color: @my-color;
+}
\ No newline at end of file
--- /dev/null
+@fat: 0;
+@cloudhead: "#000000";
+
+.nav {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity = 20);
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@fat);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr=@cloudhead, GradientType=@fat);
+}
+.evalTest(@arg) {
+ filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@arg);
+}
+.evalTest1 {
+ .evalTest(30);
+ .evalTest(5);
+}
\ No newline at end of file
--- /dev/null
+@import url("import/import-test-c.less");// import inline should not float above this #1954
+@import (inline) url("import/import-test-d.css") (min-width:600px);
+@import (inline, css) url("import/invalid-css.less");
\ No newline at end of file
--- /dev/null
+@my_theme: "test";
+
+@import "import/import-@{my_theme}-e.less";
+
+@import "import/import-@{in}@{terpolation}.less";
+
+@import "import/interpolation-vars.less";
+
--- /dev/null
+@import "import/import-once-test-c";
+@import "import/import-once-test-c";
+@import "import/import-once-test-c.less";
+@import "import/deeper/import-once-test-a";
+@import (multiple) "import/import-test-f.less";
+@import (multiple) "import/import-test-f.less";
\ No newline at end of file
--- /dev/null
+@import (reference) url("import-once.less");
+@import (reference) url("css-3.less");
+@import (reference) url("media.less");
+@import (reference) url("import/import-reference.less");
+
+.b {
+ .z();
+}
+
+.zz();
+
+.visible:extend(.z all) {
+ extend: test;
+}
+
+.test-mediaq-import {
+ .mixin-with-mediaq(340px);
+}
+
+.class:extend(.class all) {
+}
+.mixin-with-nested-selectors();
+.mixin-with-directives(some-name);
\ No newline at end of file
--- /dev/null
+@import url(http://fonts.googleapis.com/css?family=Open+Sans);
+
+@import url(/absolute/something.css) screen and (color) and (max-width: 600px);
+
+@import (optional) "file-does-not-exist.does-not-exist";
+
+@var: 100px;
+@import url("//ha.com/file.css") (min-width:@var);
+
+#import-test {
+ .mixin;
+ width: 10px;
+ height: (@a + 10%);
+}
+@import "import/import-test-e" screen and (max-width: 600px);
+
+@import url("import/import-test-a.less");
+
+@import (less, multiple) "import/import-test-d.css" screen and (max-width: 601px);
+
+@import (multiple) "import/import-test-e" screen and (max-width: 602px);
+
+@import (less, multiple) url("import/import-test-d.css") screen and (max-width: 603px);
+
+@media print {
+ @import (multiple) "import/import-test-e";
+}
+
+@charset "UTF-8"; // climb on top #2126
+
--- /dev/null
+.deep-import-url {
+ color: red;
+}
\ No newline at end of file
--- /dev/null
+@import url("url-import-2.less");
\ No newline at end of file
--- /dev/null
+@import "../import-once-test-c";
\ No newline at end of file
--- /dev/null
+@import url("deeper-2/url-import.less");
\ No newline at end of file
--- /dev/null
+@import "../css/background.css";
+@import "import-test-d.css";
+
+@import "imports/logo";
+@import "imports/font";
+
+.unquoted-relative-path-bg() {
+ background-image: url(../../data/image.jpg);
+}
+.quoted-relative-path-border-image() {
+ border-image: url('../../data/image.jpg');
+}
+
+#imported-relative-path {
+ .unquoted-relative-path-bg;
+ .quoted-relative-path-border-image;
+}
\ No newline at end of file
--- /dev/null
+@charset "ISO-8859-1";
\ No newline at end of file
--- /dev/null
+@import (inline) "imports/logo.less";
+@import "import-@{in}@{terpolation}2.less";
\ No newline at end of file
--- /dev/null
+.a {
+ var: test;
+}
+
+@in: "redefined-does-nothing";
\ No newline at end of file
--- /dev/null
+
+@c: red;
+
+#import {
+ color: @c;
+}
--- /dev/null
+.z {
+ color: red;
+ .c {
+ color: green;
+ }
+}
+.only-with-visible,
+.z {
+ color: green;
+ &:hover {
+ color: green;
+ }
+ & {
+ color: green;
+ }
+ & + & {
+ color: green;
+ .sub {
+ color: green;
+ }
+ }
+}
+
+& {
+ .hidden {
+ hidden: true;
+ }
+}
+
+@media tv {
+ .hidden {
+ hidden: true;
+ }
+}
+
+/* comment is not output */
+
+.zz {
+ .y {
+ pulled-in: yes;
+ }
+ /* comment pulled in */
+}
+@max-size: 450px;
+.mixin-with-mediaq(@num) {
+ color: green;
+ test: @num;
+ @media (max-size: @max-size) {
+ color: red;
+ }
+}
+//https://github.com/less/less.js/issues/2359
+@supports (something: else) {
+ .class {
+ something: else;
+ }
+ .nestedToo {
+ .class {
+ something: else;
+ }
+ }
+ .invisible {
+ something: else;
+ }
+}
+//https://github.com/less/less.js/issues/1979
+.mixin-with-nested-selectors() {
+ .test {
+ color: red;
+ &:first-child {
+ color: blue;
+ }
+ }
+}
+.mixin-with-directives(@keyframeName) {
+ @keyframes @keyframeName {
+ @rules1();
+ }
+ @supports (animation-name: test) {
+ @keyframes @keyframeName {
+ @rules2();
+ }
+ .selector {
+ color: red;
+ }
+ }
+ @rules1: {property: value;};
+ @rules2: {property: value;};
+}
\ No newline at end of file
--- /dev/null
+@import "import-test-b.less";
+@import url("deeper/url-import.less");
+@a: 20%;
+@import "urls.less";
\ No newline at end of file
--- /dev/null
+@import "import-test-c";
+
+@b: 100%;
+
+.mixin {
+ height: 10px;
+ color: @c;
+}
--- /dev/null
+
+@c: red;
+
+#import {
+ color: @c;
+}
--- /dev/null
+#css { color: yellow; }
--- /dev/null
+
+body { width: 100% }
--- /dev/null
+@import "import-test-e";
+
+.test-f {
+ height: 10px;
+}
--- /dev/null
+@font-face {
+ font-family: xecret;
+ src: url('../assets/xecret.ttf');
+}
+
+#secret {
+ font-family: xecret, sans-serif;
+}
--- /dev/null
+#logo {
+ width: 100px;
+ height: 100px;
+ background: url('../assets/logo.png');
+ background: url("#inline-svg");
+}
--- /dev/null
+@in: "in";
+@terpolation: "terpolation";
+
+// should be ignored because its already imported
+// and it uses a variable from the parent scope
+@import "import-@{my_theme}-e.less";
--- /dev/null
+this isn't very valid CSS.
\ No newline at end of file
--- /dev/null
+// empty file showing that it loads from the relative path first
--- /dev/null
+@import "import-test-e";
+
+data-uri {
+ property: data-uri('image.svg');
+}
+
--- /dev/null
+.eval {
+ js: `42`;
+ js: `1 + 1`;
+ js: `"hello world"`;
+ js: `[1, 2, 3]`;
+ title: `typeof process.title`;
+ ternary: `(1 + 1 == 2 ? true : false)`;
+ multiline: `(function(){var x = 1 + 1;
+ return x})()`;
+}
+.scope {
+ @foo: 42;
+ var: `parseInt(this.foo.toJS())`;
+ escaped: ~`2 + 5 + 'px'`;
+}
+.vars {
+ @var: `4 + 4`;
+ width: @var;
+}
+.escape-interpol {
+ @world: "world";
+ width: ~`"hello" + " " + @{world}`;
+}
+.arrays {
+ @ary: 1, 2, 3;
+ @ary2: 1 2 3;
+ ary: `@{ary}.join(', ')`;
+ ary1: `@{ary2}.join(', ')`;
+}
+.transitions(...) {
+ @arg: ~`"@{arguments}".replace(/[\[\]]*/g, '')`;
+ 1: @arg; // rounded to integers
+ 2: ~`"@{arguments}"`; // rounded to integers
+ 3: @arguments; // OK
+}
+.test-tran {
+ .transitions(opacity 0.3s ease-in 0.3s, max-height 0.6s linear, margin-bottom 0.4s linear;);
+}
--- /dev/null
+@var: @a;
+@a: 100%;
+
+.lazy-eval {
+ width: @var;
+}
--- /dev/null
+@media (-o-min-device-pixel-ratio: 2/1) {
+ .test-math-and-units {
+ font: ignores 0/0 rules;
+ test-division: 4 / 2 + 5em;
+ simple: 1px + 1px;
+ }
+}
\ No newline at end of file
--- /dev/null
+
+// For now, variables can't be declared inside @media blocks.
+
+@var: 42;
+
+@media print {
+ .class {
+ color: blue;
+ .sub {
+ width: @var;
+ }
+ }
+ .top, header > h1 {
+ color: (#222 * 2);
+ }
+}
+
+@media screen {
+ @base: 8;
+ body { max-width: (@base * 60); }
+}
+
+@ratio_large: 16;
+@ratio_small: 9;
+
+@media all and (device-aspect-ratio: @ratio_large / @ratio_small) {
+ body { max-width: 800px; }
+}
+
+@media all and (orientation:portrait) {
+ aside { float: none; }
+}
+
+@media handheld and (min-width: @var), screen and (min-width: 20em) {
+ body {
+ max-width: 480px;
+ }
+}
+
+body {
+ @media print {
+ padding: 20px;
+
+ header {
+ background-color: red;
+ }
+
+ @media (orientation:landscape) {
+ margin-left: 20px;
+ }
+ }
+}
+
+@media screen {
+ .sidebar {
+ width: 300px;
+ @media (orientation: landscape) {
+ width: 500px;
+ }
+ }
+}
+
+@media a {
+ .first {
+ @media b {
+ .second {
+ .third {
+ width: 300px;
+ @media c {
+ width: 500px;
+ }
+ }
+ .fourth {
+ width: 3;
+ }
+ }
+ }
+ }
+}
+
+body {
+ @media a, b and c {
+ width: 95%;
+
+ @media x, y {
+ width: 100%;
+ }
+ }
+}
+
+.mediaMixin(@fallback: 200px) {
+ background: black;
+
+ @media handheld {
+ background: white;
+
+ @media (max-width: @fallback) {
+ background: red;
+ }
+ }
+}
+
+.a {
+ .mediaMixin(100px);
+}
+
+.b {
+ .mediaMixin();
+}
+@smartphone: ~"only screen and (max-width: 200px)";
+@media @smartphone {
+ body {
+ width: 480px;
+ }
+}
+
+@media print {
+ @page :left {
+ margin: 0.5cm;
+ }
+ @page :right {
+ margin: 0.5cm;
+ }
+ @page Test:first {
+ margin: 1cm;
+ }
+ @page :first {
+ size: 8.5in 11in;
+ @top-left {
+ margin: 1cm;
+ }
+ @top-left-corner {
+ margin: 1cm;
+ }
+ @top-center {
+ margin: 1cm;
+ }
+ @top-right {
+ margin: 1cm;
+ }
+ @top-right-corner {
+ margin: 1cm;
+ }
+ @bottom-left {
+ margin: 1cm;
+ }
+ @bottom-left-corner {
+ margin: 1cm;
+ }
+ @bottom-center {
+ margin: 1cm;
+ }
+ @bottom-right {
+ margin: 1cm;
+ }
+ @bottom-right-corner {
+ margin: 1cm;
+ }
+ @left-top {
+ margin: 1cm;
+ }
+ @left-middle {
+ margin: 1cm;
+ }
+ @left-bottom {
+ margin: 1cm;
+ }
+ @right-top {
+ margin: 1cm;
+ }
+ @right-middle {
+ content: "Page " counter(page);
+ }
+ @right-bottom {
+ margin: 1cm;
+ }
+ }
+}
+
+@media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 128dpcm) {
+ .b {
+ background: red;
+ }
+}
+
+.bg() {
+ background: red;
+
+ @media (max-width: 500px) {
+ background: green;
+ }
+}
+
+body {
+ .bg();
+}
+
+@bpMedium: 1000px;
+@media (max-width: @bpMedium) {
+ body {
+ .bg();
+ background: blue;
+ }
+}
+
+@media (max-width: 1200px) {
+ /* a comment */
+
+ @media (max-width: 900px) {
+ body { font-size: 11px; }
+ }
+}
+
+.nav-justified {
+ @media (min-width: 480px) {
+ > li {
+ display: table-cell;
+ }
+ }
+}
+
+.menu
+{
+ @media (min-width: 768px) {
+ .nav-justified();
+ }
+}
+@all: ~"all";
+@tv: ~"tv";
+@media @all and @tv {
+ .all-and-tv-variables {
+ var: all-and-tv;
+ }
+}
\ No newline at end of file
--- /dev/null
+.first-transform() {
+ transform+: rotate(90deg), skew(30deg);
+}
+.second-transform() {
+ transform+: scale(2,4);
+}
+.third-transform() {
+ transform: scaleX(45deg);
+}
+.fourth-transform() {
+ transform+: scaleX(45deg);
+}
+.fifth-transform() {
+ transform+: scale(2,4) !important;
+}
+.first-background() {
+ background+: url(data://img1.png);
+}
+.second-background() {
+ background+: url(data://img2.png);
+}
+
+.test1 {
+ // Can merge values
+ .first-transform();
+ .second-transform();
+}
+.test2 {
+ // Wont merge values without +: merge directive, for backwards compatibility with css
+ .first-transform();
+ .third-transform();
+}
+.test3 {
+ // Wont merge values from two sources with different properties
+ .fourth-transform();
+ .first-background();
+}
+.test4 {
+ // Wont merge values from sources that merked as !important, for backwards compatibility with css
+ .first-transform();
+ .fifth-transform();
+}
+.test5 {
+ // Wont merge values from mixins that merked as !important, for backwards compatibility with css
+ .first-transform();
+ .second-transform() !important;
+}
+.test6 {
+ // Ignores !merge if no peers found
+ .second-transform();
+}
+
+.test-interleaved {
+ transform+: t1;
+ background+: b1;
+ transform+: t2;
+ background+: b2, b3;
+ transform+: t3;
+}
+
+.test-spaced {
+ transform+_: t1;
+ background+_: b1;
+ transform+_: t2;
+ background+_: b2, b3;
+ transform+_: t3;
+}
+
+.test-interleaved-with-spaced {
+ transform+_: t1s;
+ transform+: t2;
+ background+: b1;
+ transform+_: t3s;
+ transform+: t4 t5s;
+ background+_: b2s, b3;
+ transform+_: t6s;
+ background+: b4;
+}
--- /dev/null
+.mixin (@a: 1px, @b: 50%) {
+ width: (@a * 5);
+ height: (@b - 1%);
+}
+
+.mixina (@style, @width, @color: black) {
+ border: @width @style @color;
+}
+
+.mixiny
+(@a: 0, @b: 0) {
+ margin: @a;
+ padding: @b;
+}
+
+.hidden() {
+ color: transparent; // asd
+}
+
+#hidden {
+ .hidden;
+}
+
+#hidden1 {
+ .hidden();
+}
+
+.two-args {
+ color: blue;
+ .mixin(2px, 100%);
+ .mixina(dotted, 2px);
+}
+
+.one-arg {
+ .mixin(3px);
+}
+
+.no-parens {
+ .mixin;
+}
+
+.no-args {
+ .mixin();
+}
+
+.var-args {
+ @var: 9;
+ .mixin(@var, (@var * 2));
+}
+
+.multi-mix {
+ .mixin(2px, 30%);
+ .mixiny(4, 5);
+}
+
+.maxa(@arg1: 10, @arg2: #f00) {
+ padding: (@arg1 * 2px);
+ color: @arg2;
+}
+
+body {
+ .maxa(15);
+}
+
+@glob: 5;
+.global-mixin(@a:2) {
+ width: (@glob + @a);
+}
+
+.scope-mix {
+ .global-mixin(3);
+}
+
+.nested-ruleset (@width: 200px) {
+ width: @width;
+ .column { margin: @width; }
+}
+.content {
+ .nested-ruleset(600px);
+}
+
+//
+
+.same-var-name2(@radius) {
+ radius: @radius;
+}
+.same-var-name(@radius) {
+ .same-var-name2(@radius);
+}
+#same-var-name {
+ .same-var-name(5px);
+}
+
+//
+
+.var-inside () {
+ @var: 10px;
+ width: @var;
+}
+#var-inside { .var-inside; }
+
+.mixin-arguments (@width: 0px, ...) {
+ border: @arguments;
+ width: @width;
+}
+
+.arguments {
+ .mixin-arguments(1px, solid, black);
+}
+.arguments2 {
+ .mixin-arguments();
+}
+.arguments3 {
+ .mixin-arguments;
+}
+
+.mixin-arguments2 (@width, @rest...) {
+ border: @arguments;
+ rest: @rest;
+ width: @width;
+}
+.arguments4 {
+ .mixin-arguments2(0, 1, 2, 3, 4);
+}
+
+// Edge cases
+
+.edge-case {
+ .mixin-arguments("{");
+}
+
+// Division vs. Literal Slash
+.border-radius(@r: 2px/5px) {
+ border-radius: @r;
+}
+.slash-vs-math {
+ .border-radius();
+ .border-radius(5px/10px);
+ .border-radius((3px * 2));
+}
+// semi-colon vs comma for delimiting
+
+.mixin-takes-one(@a) {
+ one: @a;
+}
+
+.mixin-takes-two(@a; @b) {
+ one: @a;
+ two: @b;
+}
+
+.comma-vs-semi-colon {
+ .mixin-takes-two(@a : a; @b : b, c);
+ .mixin-takes-two(@a : d, e; @b : f);
+ .mixin-takes-one(@a: g);
+ .mixin-takes-one(@a : h;);
+ .mixin-takes-one(i);
+ .mixin-takes-one(j;);
+ .mixin-takes-two(k, l);
+ .mixin-takes-one(m, n;);
+ .mixin-takes-two(o, p; q);
+ .mixin-takes-two(r, s; t;);
+}
+
+.mixin-conflict(@a:defA, @b:defB, @c:defC) {
+ three: @a, @b, @c;
+}
+
+.mixin-conflict(@a:defA, @b:defB, @c:defC, @d:defD) {
+ four: @a, @b, @c, @d;
+}
+
+#named-conflict {
+ .mixin-conflict(11, 12, 13, @a:a);
+ .mixin-conflict(@a:a, 21, 22, 23);
+}
+@a: 3px;
+.mixin-default-arg(@a: 1px, @b: @a, @c: @b) {
+ defaults: 1px 1px 1px;
+ defaults: 2px 2px 2px;
+}
+
+.test-mixin-default-arg {
+ .mixin-default-arg();
+ .mixin-default-arg(2px);
+}
+
+.mixin-comma-default1(@color; @padding; @margin: 2, 2, 2, 2) {
+ margin: @margin;
+}
+.selector {
+ .mixin-comma-default1(#33acfe; 4);
+}
+.mixin-comma-default2(@margin: 2, 2, 2, 2;) {
+ margin: @margin;
+}
+.selector2 {
+ .mixin-comma-default2();
+}
+.mixin-comma-default3(@margin: 2, 2, 2, 2) {
+ margin: @margin;
+}
+.selector3 {
+ .mixin-comma-default3(4,2,2,2);
+}
+
+.test-calling-one-arg-mixin(@a) {
+}
+
+.test-calling-one-arg-mixin(@a, @b, @rest...) {
+}
+
+div {
+ .test-calling-one-arg-mixin(1);
+}
\ No newline at end of file
--- /dev/null
+.scope {
+ @var: 99px;
+ .mixin () {
+ width: @var;
+ }
+}
+
+.class {
+ .scope > .mixin;
+}
+
+.overwrite {
+ @var: 0px;
+ .scope > .mixin;
+}
+
+.nested {
+ @var: 5px;
+ .mixin () {
+ width: @var;
+ }
+ .class {
+ @var: 10px;
+ .mixin;
+ }
+}
--- /dev/null
+
+// basics:
+
+guard-default-basic-1 {
+ .m(1) {case: 1}
+ .m(@x) when (default()) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+guard-default-basic-2 {
+ .m(1) {case: 1}
+ .m(2) {case: 2}
+ .m(3) {case: 3}
+ .m(@x) when (default()) {default: @x}
+
+ &-0 {.m(0)}
+ &-2 {.m(2)}
+}
+
+guard-default-basic-3 {
+ .m(@x) when (@x = 1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (@x = 3) {case: 3}
+ .m(@x) when (default()) {default: @x}
+
+ &-0 {.m(0)}
+ &-2 {.m(2)}
+ &-3 {.m(3)}
+}
+
+guard-default-definition-order {
+ .m(@x) when (default()) {default: @x}
+ .m(@x) when (@x = 1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (@x = 3) {case: 3}
+
+ &-0 {.m(0)}
+ &-2 {.m(2)}
+ &-2 {.m(3)}
+}
+
+// out of guard:
+
+guard-default-out-of-guard {
+ .m(1) {case-1: 1}
+ .m(@x: default()) when (default()) {default: @x}
+
+ &-0 {
+ case-0: default();
+ .m(1);
+ .m(2);
+ case-2: default();
+ }
+ &-1 {.m(default())}
+ &-2 {.m()}
+}
+
+// expressions:
+
+guard-default-expr-not {
+ .m(1) {case: 1}
+ .m(@x) when not(default()) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+guard-default-expr-eq {
+ .m(@x) when (@x = true) {case: @x}
+ .m(@x) when (@x = false) {case: @x}
+ .m(@x) when (@x = default()) {default: @x}
+
+ &-true {.m(true)}
+ &-false {.m(false)}
+}
+
+guard-default-expr-or {
+ .m(1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (default()), (@x = 2) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+ &-3 {.m(3)}
+}
+
+guard-default-expr-and {
+ .m(1) {case: 1}
+ .m(2) {case: 2}
+ .m(@x) when (default()) and (@x = 3) {default: @x}
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+ &-3 {.m(3)}
+ &-4 {.m(4)}
+}
+
+guard-default-expr-always {
+ .m(1) {case: 1}
+ .m(@x) when (default()), not(default()) {default: @x} // always match
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+guard-default-expr-never {
+ .m(1) {case: 1}
+ .m(@x) when (default()) and not(default()) {default: @x} // never match
+
+ &-1 {.m(1)}
+ &-2 {.m(2)}
+}
+
+
+// not conflicting multiple default() uses:
+
+guard-default-multi-1 {
+ .m(0) {case: 0}
+ .m(@x) when (default()) {default-1: @x}
+ .m(2) when (default()) {default-2: @x}
+
+ &-0 {.m(0)}
+ &-1 {.m(1)}
+}
+
+guard-default-multi-2 {
+ .m(1, @x) when (default()) {default-1: @x}
+ .m(2, @x) when (default()) {default-2: @x}
+ .m(@x, yes) when (default()) {default-3: @x}
+
+ &-1 {.m(1, no)}
+ &-2 {.m(2, no)}
+ &-3 {.m(3, yes)}
+}
+
+guard-default-multi-3 {
+ .m(red) {case-1: darkred}
+ .m(blue) {case-2: darkblue}
+ .m(@x) when (iscolor(@x)) and (default()) {default-color: @x}
+ .m('foo') {case-1: I am 'foo'}
+ .m('bar') {case-2: I am 'bar'}
+ .m(@x) when (isstring(@x)) and (default()) {default-string: I am @x}
+
+ &-blue {.m(blue)}
+ &-green {.m(green)}
+ &-foo {.m('foo')}
+ &-baz {.m('baz')}
+}
+
+guard-default-multi-4 {
+ .m(@x) when (default()), not(default()) {always: @x}
+ .m(@x) when (default()) and not(default()) {never: @x}
+ .m(2) {case: 2}
+
+ .m(1);
+ .m(2);
+}
+
+guard-default-not-ambiguos-2 {
+ .m(@x) {case: 1}
+ .m(@x) when (default()) {default: @x}
+ .m(@x) when not(default()) {not-default: @x}
+
+ .m(2);
+}
+
+guard-default-not-ambiguos-3 {
+ .m(@x) {case: 1}
+ .m(@x) when not(default()) {not-default-1: @x}
+ .m(@x) when not(default()) {not-default-2: @x}
+
+ .m(2);
+}
+
+// default & scope
+
+guard-default-scopes {
+ .s1() {.m(@v) {1: no condition}}
+ .s2() {.m(@v) when (@v) {2: when true}}
+ .s3() {.m(@v) when (default()) {3: when default}}
+
+ &-3 {
+ .s2();
+ .s3();
+ .m(false);
+ }
+
+ &-1 {
+ .s1();
+ .s3();
+ .m(false);
+ }
+}
--- /dev/null
+
+// Stacking, functions..
+
+.light (@a) when (lightness(@a) > 50%) {
+ color: white;
+}
+.light (@a) when (lightness(@a) < 50%) {
+ color: black;
+}
+.light (@a) {
+ margin: 1px;
+}
+
+.light1 { .light(#ddd) }
+.light2 { .light(#444) }
+
+// Arguments against each other
+
+.max (@a, @b) when (@a > @b) {
+ width: @a;
+}
+.max (@a, @b) when (@a < @b) {
+ width: @b;
+}
+
+.max1 { .max(3, 6) }
+.max2 { .max(8, 1) }
+
+// Globals inside guards
+
+@g: auto;
+
+.glob (@a) when (@a = @g) {
+ margin: @a @g;
+}
+.glob1 { .glob(auto) }
+
+// Other operators
+
+.ops (@a) when (@a >= 0) {
+ height: gt-or-eq;
+}
+.ops (@a) when (@a =< 0) {
+ height: lt-or-eq;
+}
+.ops (@a) when (@a <= 0) {
+ height: lt-or-eq-alias;
+}
+.ops (@a) when not(@a = 0) {
+ height: not-eq;
+}
+.ops1 { .ops(0) }
+.ops2 { .ops(1) }
+.ops3 { .ops(-1) }
+
+// Scope and default values
+
+@a: auto;
+
+.default (@a: inherit) when (@a = inherit) {
+ content: default;
+}
+.default1 { .default }
+
+// true & false keywords
+.test (@a) when (@a) {
+ content: "true.";
+}
+.test (@a) when not (@a) {
+ content: "false.";
+}
+
+.test1 { .test(true) }
+.test2 { .test(false) }
+.test3 { .test(1) }
+.test4 { .test(boo) }
+.test5 { .test("true") }
+
+// Boolean expressions
+
+.bool () when (true) and (false) { content: true and false } // FALSE
+.bool () when (true) and (true) { content: true and true } // TRUE
+.bool () when (true) { content: true } // TRUE
+.bool () when (false) and (false) { content: true } // FALSE
+.bool () when (false), (true) { content: false, true } // TRUE
+.bool () when (false) and (true) and (true), (true) { content: false and true and true, true } // TRUE
+.bool () when (true) and (true) and (false), (false) { content: true and true and false, false } // FALSE
+.bool () when (false), (true) and (true) { content: false, true and true } // TRUE
+.bool () when (false), (false), (true) { content: false, false, true } // TRUE
+.bool () when (false), (false) and (true), (false) { content: false, false and true, false } // FALSE
+.bool () when (false), (true) and (true) and (true), (false) { content: false, true and true and true, false } // TRUE
+.bool () when not (false) { content: not false }
+.bool () when not (true) and not (false) { content: not true and not false }
+.bool () when not (true) and not (true) { content: not true and not true }
+.bool () when not (false) and (false), not (false) { content: not false and false, not false }
+
+.bool1 { .bool }
+
+.equality-unit-test(@num) when (@num = 1%) {
+ test: fail;
+}
+.equality-unit-test(@num) when (@num = 2) {
+ test: pass;
+}
+.equality-units {
+ .equality-unit-test(1px);
+ .equality-unit-test(2px);
+}
+
+.colorguard(@col) when (@col = red) { content: is @col; }
+.colorguard(@col) when not (blue = @col) { content: is not blue its @col; }
+.colorguard(@col) {}
+.colorguardtest {
+ .colorguard(red);
+ .colorguard(blue);
+ .colorguard(purple);
+}
+
+.stringguard(@str) when (@str = "theme1") { content: @str is "theme1"; }
+.stringguard(@str) when not ("theme2" = @str) { content: @str is not "theme2"; }
+.stringguard(@str) when (@str = 'theme1') { content: @str is 'theme1'; }
+.stringguard(@str) when not ('theme2' = @str) { content: @str is not 'theme2'; }
+.stringguard(@str) when (~"theme1" = @str) { content: @str is theme1; }
+.stringguard(@str) {}
+.stringguardtest {
+ .stringguard("theme1");
+ .stringguard("theme2");
+ .stringguard('theme1');
+ .stringguard('theme2');
+ .stringguard(theme1);
+}
+
+.generic(@a, @b) {/**/}
+.generic(@a, @b) when (@a = @b) {content: @a is equal to @b}
+.generic(@a, @b) when (@b = @a) {content: @b is equal to @a too}
+.generic(@a, @b) when (@a < @b) {content: @a is less than @b}
+.generic(@a, @b) when (@b < @a) {content: @b is less than @a too}
+.generic(@a, @b) when (@a > @b) {content: @a is greater than @b}
+.generic(@a, @b) when (@b > @a) {content: @b is greater than @a too}
+.generic(@a, @b) when not(@a = @b) {content: @a is not equal to @b}
+.generic(@a, @b) when not(@b = @a) {content: @b is not equal to @a too}
+
+.variouse-types-comparison {
+ .generic(true, false);
+ .generic(1, true);
+ .generic(2, 2px);
+ .generic(3, ~"3");
+ .generic(5, ~"4");
+ .generic(abc, ~"abc");
+ .generic(abc, "abc");
+ .generic('abc', "abd");
+ .generic(6, e("6"));
+ .generic(`9`, 8);
+ .generic(a, b);
+ .generic(1 2, 3);
+}
+
+.list-comparison {
+ .generic(a b c, a b c);
+ .generic(a b c, a b d);
+ .generic(a, b, c; a, b, c);
+ .generic(a, b, c; a, b, d);
+ .generic(1 2px 300ms, 1em 2 .3s);
+
+ @space-list: 1 2 3;
+ @comma-list: 1, 2, 3;
+ @compound: @space-list @comma-list;
+
+ .generic(@space-list, @comma-list);
+ .generic(@comma-list, ~"1, 2, 3");
+ .generic(@compound, @space-list @comma-list);
+ .generic(@compound, @comma-list @space-list);
+ .generic(@compound 4, ~"1 2 3 1, 2, 3 4");
+}
+
+.mixin(...) {
+ catch:all;
+}
+.mixin(@var) when (@var=4) {
+ declare: 4;
+}
+.mixin(@var) when (@var=4px) {
+ declare: 4px;
+}
+#tryNumberPx {
+ .mixin(4px);
+}
+
+.lock-mixin(@a) {
+ .inner-locked-mixin(@x: @a) when (@a = 1) {
+ a: @a;
+ x: @x;
+ }
+}
+.call-lock-mixin {
+ .lock-mixin(1);
+ .call-inner-lock-mixin {
+ .inner-locked-mixin();
+ }
+}
+.bug-100cm-1m(@a) when (@a = 1) {
+ .failed {
+ one-hundred: not-equal-to-1;
+ }
+}
+.bug-100cm-1m(100cm);
+
+#ns {
+ .mixin-for-root-usage(@a) when (@a > 0) {
+ .mixin-generated-class {
+ a: @a;
+ }
+ }
+}
+
+#ns > .mixin-for-root-usage(1);
+
+@namespaceGuard: 1;
+#guarded when (@namespaceGuard>0) {
+ #deeper {
+ .mixin() {
+ guarded: namespace;
+ }
+ }
+}
+#guarded() when (@namespaceGuard>0) {
+ #deeper {
+ .mixin() {
+ silent: namespace;
+ }
+ }
+}
+#guarded(@variable) when (@namespaceGuard>0) {
+ #deeper {
+ .mixin() {
+ should: not match because namespace argument;
+ }
+ }
+}
+#guarded(@variable: default) when (@namespaceGuard>0) {
+ #deeper {
+ .mixin() {
+ guarded: with default;
+ }
+ }
+}
+#guarded when (@namespaceGuard<0) {
+ #deeper {
+ .mixin() {
+ should: not match because namespace guard;
+ }
+ }
+}
+#guarded-caller {
+ #guarded > #deeper > .mixin();
+}
+#top {
+ #deeper when (@namespaceGuard<0) {
+ .mixin(@a) {
+ should: not match because namespace guard;
+ }
+ }
+ #deeper() when (@namespaceGuard>0) {
+ .mixin(@a) {
+ should: match @a;
+ }
+ }
+}
+#guarded-deeper {
+ #top > #deeper > .mixin(1);
+}
--- /dev/null
+.submixin(@a) {
+ border-width: @a;
+}
+.mixin (9) {
+ border: 9 !important;
+}
+.mixin (@a: 0) {
+ border: @a;
+ boxer: @a;
+ .inner {
+ test: @a;
+ }
+ // comment
+ .submixin(@a);
+}
+
+.class {
+ .mixin(1);
+ .mixin(2) !important;
+ .mixin(3);
+ .mixin(4) !important;
+ .mixin(5);
+ .mixin !important;
+ .mixin(9);
+}
+.size(@aaa: auto) {
+ .set-width(@aaa) {
+ width: @aaa;
+ }
+ .set-width(@aaa);
+}
+.when-calling-nested-issue-2394 {
+ .size() !important;
+}
+.when-calling-nested-with-param-issue-2394 {
+ .size(10px) !important;
+}
+.testMixin-2421 () {
+ .topCheck-2421 () {
+ .nestedCheck-2421() {
+ margin: 5px;
+ }
+ .nestedCheck-2421();
+ }
+ .topCheck-2421();
+}
+.class1-2421 {
+ .testMixin-2421() !important;
+}
+.class2-2421 {
+ .testMixin-2421();
+}
+
--- /dev/null
+
+@a0: 123;
+@a1: foo;
+@a2: ~".foo";
+@a4: ~"#foo";
+
+.@{a0} {
+ a: 0;
+}
+
+.@{a1} {
+ a: 1;
+}
+
+@{a2} {
+ a: 2;
+}
+
+#@{a1} {
+ a: 3;
+}
+
+@{a4} {
+ a: 4;
+}
+
+mi-test-a {
+ .123;
+ .foo;
+ #foo;
+}
+
+.b .bb {
+ &.@{a1}-xxx .yyy-@{a1}@{a4} {
+ & @{a2}.bbb {
+ b: 1;
+ }
+ }
+}
+
+mi-test-b {
+ .b.bb.foo-xxx.yyy-foo#foo.foo.bbb;
+}
+
+@c1: @a1;
+@c2: bar;
+@c3: baz;
+
+#@{c1}-foo {
+ > .@{c2} {
+ .@{c3} {
+ c: c;
+ }
+ }
+}
+
+mi-test-c {
+ &-1 {#foo-foo;}
+ &-2 {#foo-foo > .bar;}
+ &-3 {#foo-foo > .bar.baz;}
+}
+
+.Person(@name, @gender_) {
+ .@{name} {
+ @gender: @gender_;
+ .sayGender() {
+ gender: @gender;
+ }
+ }
+}
+
+mi-test-d {
+ .Person(person, "Male");
+ .person.sayGender();
+}
--- /dev/null
+.mixin (@a: 1px, @b: 50%) {
+ width: (@a * 5);
+ height: (@b - 1%);
+ args: @arguments;
+}
+.mixin (@a: 1px, @b: 50%) when (@b > 75%){
+ text-align: center;
+}
+
+.named-arg {
+ color: blue;
+ .mixin(@b: 100%);
+}
+
+.class {
+ @var: 20%;
+ .mixin(@b: @var);
+}
+
+.all-args-wrong-args {
+ .mixin(@b: 10%, @a: 2px);
+}
+
+.mixin2 (@a: 1px, @b: 50%, @c: 50) {
+ width: (@a * 5);
+ height: (@b - 1%);
+ color: (#000000 + @c);
+}
+
+.named-args2 {
+ .mixin2(3px, @c: 100);
+}
+
+.named-args3 {
+ .mixin2(@b: 30%, @c: #123456);
+}
\ No newline at end of file
--- /dev/null
+.mix-inner (@var) {
+ border-width: @var;
+}
+
+.mix (@a: 10) {
+ .inner {
+ height: (@a * 10);
+
+ .innest {
+ width: @a;
+ .mix-inner((@a * 2));
+ }
+ }
+}
+
+.class {
+ .mix(30);
+}
+
+.class2 {
+ .mix(60);
+}
--- /dev/null
+.mixin (...) {
+ variadic: true;
+}
+.mixin (@a...) {
+ named-variadic: true;
+}
+.mixin () {
+ zero: 0;
+}
+.mixin (@a: 1px) {
+ one: 1;
+}
+.mixin (@a) {
+ one-req: 1;
+}
+.mixin (@a: 1px, @b: 2px) {
+ two: 2;
+}
+
+.mixin (@a, @b, @c) {
+ three-req: 3;
+}
+
+.mixin (@a: 1px, @b: 2px, @c: 3px) {
+ three: 3;
+}
+
+.zero {
+ .mixin();
+}
+
+.one {
+ .mixin(1);
+}
+
+.two {
+ .mixin(1, 2);
+}
+
+.three {
+ .mixin(1, 2, 3);
+}
+
+//
+
+.mixout ('left') {
+ left: 1;
+}
+
+.mixout ('right') {
+ right: 1;
+}
+
+.left {
+ .mixout('left');
+}
+.right {
+ .mixout('right');
+}
+
+//
+
+.border (@side, @width) {
+ color: black;
+ .border-side(@side, @width);
+}
+.border-side (left, @w) {
+ border-left: @w;
+}
+.border-side (right, @w) {
+ border-right: @w;
+}
+
+.border-right {
+ .border(right, 4px);
+}
+.border-left {
+ .border(left, 4px);
+}
+
+//
+
+
+.border-radius (@r) {
+ both: (@r * 10);
+}
+.border-radius (@r, left) {
+ left: @r;
+}
+.border-radius (@r, right) {
+ right: @r;
+}
+
+.only-right {
+ .border-radius(33, right);
+}
+.only-left {
+ .border-radius(33, left);
+}
+.left-right {
+ .border-radius(33);
+}
--- /dev/null
+.mixin { border: 1px solid black; }
+.mixout { border-color: orange; }
+.borders { border-style: dashed; }
+.mixin > * { border: do not match me; }
+
+#namespace {
+ .borders {
+ border-style: dotted;
+ }
+ .biohazard {
+ content: "death";
+ .man {
+ color: transparent;
+ }
+ }
+}
+#theme {
+ > .mixin {
+ background-color: grey;
+ }
+}
+#container {
+ color: black;
+ .mixin;
+ .mixout;
+ #theme > .mixin;
+}
+
+#header {
+ .milk {
+ color: white;
+ .mixin;
+ #theme > .mixin;
+ }
+ #cookie {
+ .chips {
+ #namespace .borders;
+ .calories {
+ #container;
+ }
+ }
+ .borders;
+ }
+}
+.secure-zone { #namespace .biohazard .man; }
+.direct {
+ #namespace > .borders;
+}
+
+.bo, .bar {
+ width: 100%;
+}
+.bo {
+ border: 1px;
+}
+.ar.bo.ca {
+ color: black;
+}
+.jo.ki {
+ background: none;
+}
+.amp {
+ &.support {
+ color: orange;
+ .higher {
+ top: 0px;
+ }
+ &.deeper {
+ height: auto;
+ }
+ }
+}
+.extended {
+ .bo;
+ .jo.ki;
+ .amp.support;
+ .amp.support.higher;
+ .amp.support.deeper;
+}
+.do .re .mi .fa {
+ .sol .la {
+ .si {
+ color: cyan;
+ }
+ }
+}
+.mutli-selector-parents {
+ .do.re.mi.fa.sol.la.si;
+}
+.foo .bar {
+ .bar;
+}
+.has_parents() {
+ & .underParents {
+ color: red;
+ }
+}
+.has_parents();
+.parent {
+ .has_parents();
+}
+.margin_between(@above, @below) {
+ * + & { margin-top: @above; }
+ legend + & { margin-top: 0; }
+ & + * { margin-top: @below; }
+}
+h1 { .margin_between(25px, 10px); }
+h2 { .margin_between(20px, 8px); }
+h3 { .margin_between(15px, 5px); }
+
+.mixin_def(@url, @position){
+ background-image: @url;
+ background-position: @position;
+}
+.error{
+ @s: "/";
+ .mixin_def( "@{s}a.png", center center);
+}
+.recursion() {
+ color: black;
+}
+.test-rec {
+ .recursion {
+ .recursion();
+ }
+}
+.paddingFloat(@padding) { padding-left: @padding; }
+
+.button {
+ .paddingFloat(((10px + 12) * 2));
+
+ &.large { .paddingFloat(((10em * 2) * 2)); }
+}
+.clearfix() {
+ // ...
+}
+.clearfix {
+ .clearfix();
+}
+.clearfix {
+ .clearfix();
+}
+.foo {
+ .clearfix();
+}
\ No newline at end of file
--- /dev/null
+{
+ "the-border": "1px",
+ "base-color": "#111",
+ "red": "#842210"
+}
\ No newline at end of file
--- /dev/null
+#header {
+ color: (@base-color * 3);
+ border-left: @the-border;
+ border-right: (@the-border * 2);
+}
+#footer {
+ color: (@base-color + #003300);
+ border-color: @red;
+}
+@red: blue; // var is overridden by the modifyVars
+//@base-color: green;
\ No newline at end of file
--- /dev/null
+@import "svg-gradient-mixin.less";
+
+.gray-gradient {
+ .gradient-mixin(#999);
+}
--- /dev/null
+.gradient-mixin(@color) {
+ background: svg-gradient(to bottom,
+ fade(@color, 0%) 0%,
+ fade(@color, 5%) 60%,
+ fade(@color, 10%) 70%,
+ fade(@color, 15%) 73%,
+ fade(@color, 20%) 75%,
+ fade(@color, 25%) 80%,
+ fade(@color, 30%) 85%,
+ fade(@color, 35%) 88%,
+ fade(@color, 40%) 90%,
+ fade(@color, 45%) 95%,
+ fade(@color, 50%) 100%
+ );
+}
--- /dev/null
+.a {
+ a: `1 + 1`;
+}
\ No newline at end of file
--- /dev/null
+SyntaxError: You are using JavaScript, which has been disabled. in {path}no-js-errors.less on line 2, column 6:
+1 .a {
+2 a: `1 + 1`;
+3 }
--- /dev/null
+.mixin() {\r
+}
\ No newline at end of file
--- /dev/null
+#operations {
+ color: (#110000 + #000011 + #001100); // #111111
+ height: (10px / 2px + 6px - 1px * 2); // 9px
+ width: (2 * 4 - 5em); // 3em
+ .spacing {
+ height: (10px / 2px+6px-1px*2);
+ width: (2 * 4-5em);
+ }
+ substraction: (20 - 10 - 5 - 5); // 0
+ division: (20 / 5 / 4); // 1
+}
+
+@x: 4;
+@y: 12em;
+
+.with-variables {
+ height: (@x + @y); // 16em
+ width: (12 + @y); // 24em
+ size: (5cm - @x); // 1cm
+}
+
+.with-functions {
+ color: (rgb(200, 200, 200) / 2);
+ color: (2 * hsl(0, 50%, 50%));
+ color: (rgb(10, 10, 10) + hsl(0, 50%, 50%));
+}
+
+@z: -2;
+
+.negative {
+ height: (2px + @z); // 0px
+ width: (2px - @z); // 4px
+}
+
+.shorthands {
+ padding: -1px 2px 0 -4px; //
+}
+
+.rem-dimensions {
+ font-size: (20rem / 5 + 1.5rem); // 5.5rem
+}
+
+.colors {
+ color: #123; // #112233
+ border-color: (#234 + #111111); // #334455
+ background-color: (#222222 - #fff); // #000000
+ .other {
+ color: (2 * #111); // #222222
+ border-color: (#333333 / 3 + #111); // #222222
+ }
+}
+
+.negations {
+ @var: 4px;
+ variable: (-@var); // 4
+ variable1: (-@var + @var); // 0
+ variable2: (@var + -@var); // 0
+ variable3: (@var - -@var); // 8
+ variable4: (-@var - -@var); // 0
+ paren: (-(@var)); // -4px
+ paren2: (-(2 + 2) * -@var); // 16
+}
--- /dev/null
+.parens {
+ @var: 1px;
+ border: (@var * 2) solid black;
+ margin: (@var * 1) (@var + 2) (4 * 4) 3;
+ width: (6 * 6);
+ padding: 2px (6 * 6px);
+}
+
+.more-parens {
+ @var: (2 * 2);
+ padding: (2 * @var) 4 4 (@var * 1px);
+ width-all: ((@var * @var) * 6);
+ width-first: ((@var * @var)) * 6;
+ width-keep: (@var * @var) * 6;
+ height-keep: (7 * 7) + (8 * 8);
+ height-all: ((7 * 7) + (8 * 8));
+ height-parts: ((7 * 7)) + ((8 * 8));
+ margin-keep: (4 * (5 + 5) / 2) - (@var * 2);
+ margin-parts: ((4 * (5 + 5) / 2)) - ((@var * 2));
+ margin-all: ((4 * (5 + 5) / 2) + (-(@var * 2)));
+ border-radius-keep: 4px * (1 + 1) / @var + 3px;
+ border-radius-parts: ((4px * (1 + 1))) / ((@var + 3px));
+ border-radius-all: (4px * (1 + 1) / @var + 3px);
+ //margin: (6 * 6)px;
+}
+
+.negative {
+ @var: 1;
+ neg-var: -@var; // -1 ?
+ neg-var-paren: -(@var); // -(1) ?
+}
+
+.nested-parens {
+ width: 2 * (4 * (2 + (1 + 6))) - 1;
+ height: ((2 + 3) * (2 + 3) / (9 - 4)) + 1;
+}
+
+.mixed-units {
+ margin: 2px 4em 1 5pc;
+ padding: (2px + 4px) 1em 2px 2;
+}
+
+.test-false-negatives {
+ a: ~"(";
+}
--- /dev/null
+
+pi-test {
+ @prefix: ufo-;
+ @a: border;
+ @bb: top;
+ @c_c: left;
+ @d-d4: radius;
+ @-: -;
+
+ @var: ~'@not-variable';
+
+ @{a}: 0;
+ @{var}: @var;
+ @{prefix}width: 50%;
+ *-z-@{a} :1px dashed blue;
+ -www-@{a}-@{bb}: 2px;
+ @{d-d4}-is-not-a-@{a}:true;
+ @{a}-@{bb}-@{c_c}-@{d-d4} : 2em;
+ @{a}@{-}@{bb}@{-}red@{-}@{d-d4}-: 3pt;
+
+ .mixin(mixer);
+ .merge(ish, base);
+}
+
+@global: global;
+
+.mixin(@arg) {
+ @local: local;
+ @{global}-@{local}-@{arg}-property: strong;
+}
+
+.merge(@p, @v) {
+ &-merge {
+ @prefix: pre;
+ @suffix: ish;
+ @{prefix}-property-ish+ :high;
+ pre-property-@{suffix} +: middle;
+ @{prefix}-property-@{suffix}+: low;
+ @{prefix}-property-@{p} + : @v;
+
+ @subterfuge: ~'+';
+ pre-property-ish@{subterfuge}: nice try dude;
+ }
+}
+
+pi-indirect-vars {
+ @{p}: @p;
+ @p: @@a;
+ @a: b;
+ @b: auto;
+}
+
+pi-complex-values {
+ @{p}@{p}: none;
+ @p: (1 + 2px) fadeout(#ff0, 50%), pi() /* foo */;
+}
--- /dev/null
+#first > .one {
+ > #second .two > #deux {
+ width: 50%;
+ #third {
+ &:focus {
+ color: black;
+ #fifth {
+ > #sixth {
+ .seventh #eighth {
+ + #ninth {
+ color: purple;
+ }
+ }
+ }
+ }
+ }
+ height: 100%;
+ }
+ #fourth, #five, #six {
+ color: #110000;
+ .seven, .eight > #nine {
+ border: 1px solid black;
+ }
+ #ten {
+ color: red;
+ }
+ }
+ }
+ font-size: 2em;
+}
--- /dev/null
+@x: red;
+@x: blue;
+@z: transparent;
+@mix: none;
+
+.mixin {
+ @mix: #989;
+}
+@mix: blue;
+.tiny-scope {
+ color: @mix; // #989
+ .mixin;
+}
+
+.scope1 {
+ @y: orange;
+ @z: black;
+ color: @x; // blue
+ border-color: @z; // black
+ .hidden {
+ @x: #131313;
+ }
+ .scope2 {
+ @y: red;
+ color: @x; // blue
+ .scope3 {
+ @local: white;
+ color: @y; // red
+ border-color: @z; // black
+ background-color: @local; // white
+ }
+ }
+}
+
+#namespace {
+ .scoped_mixin() {
+ @local-will-be-made-global: green;
+ .scope {
+ scoped-val: @local-will-be-made-global;
+ }
+ }
+}
+
+#namespace > .scoped_mixin();
+
+.setHeight(@h) { @height: 1024px; }
+.useHeightInMixinCall(@h) { .useHeightInMixinCall { mixin-height: @h; } }
+@mainHeight: 50%;
+.setHeight(@mainHeight);
+.heightIsSet { height: @height; }
+.useHeightInMixinCall(@height);
+
+.importRuleset() {
+ .imported {
+ exists: true;
+ }
+}
+.importRuleset();
+.testImported {
+ .imported;
+}
+
+@parameterDefault: 'top level';
+@anotherVariable: 'top level';
+//mixin uses top-level variables
+.mixinNoParam(@parameter: @parameterDefault) when (@parameter = 'top level') {
+ default: @parameter;
+ scope: @anotherVariable;
+ sub-scope-only: @subScopeOnly;
+}
+
+#allAreUsedHere {
+ //redefine top-level variables in different scope
+ @parameterDefault: 'inside';
+ @anotherVariable: 'inside';
+ @subScopeOnly: 'inside';
+ //use the mixin
+ .mixinNoParam();
+}
+#parentSelectorScope {
+ @col: white;
+ & {
+ @col: black;
+ }
+ prop: @col;
+ & {
+ @col: black;
+ }
+}
+.test-empty-mixin() {
+}
+#parentSelectorScopeMixins {
+ & {
+ .test-empty-mixin() {
+ should: never seee 1;
+ }
+ }
+ .test-empty-mixin();
+ & {
+ .test-empty-mixin() {
+ should: never seee 2;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+h1, h2, h3 {
+ a, p {
+ &:hover {
+ color: red;
+ }
+ }
+}
+
+#all { color: blue; }
+#the { color: blue; }
+#same { color: blue; }
+
+ul, li, div, q, blockquote, textarea {
+ margin: 0;
+}
+
+td {
+ margin: 0;
+ padding: 0;
+}
+
+td, input {
+ line-height: 1em;
+}
+
+a {
+ color: red;
+
+ &:hover { color: blue; }
+
+ div & { color: green; }
+
+ p & span { color: yellow; }
+}
+
+.foo {
+ .bar, .baz {
+ & .qux {
+ display: block;
+ }
+ .qux & {
+ display: inline;
+ }
+ .qux& {
+ display: inline-block;
+ }
+ .qux & .biz {
+ display: none;
+ }
+ }
+}
+
+.b {
+ &.c {
+ .a& {
+ color: red;
+ }
+ }
+}
+
+.b {
+ .c & {
+ &.a {
+ color: red;
+ }
+ }
+}
+
+.p {
+ .foo &.bar {
+ color: red;
+ }
+}
+
+.p {
+ .foo&.bar {
+ color: red;
+ }
+}
+
+.foo {
+ .foo + & {
+ background: amber;
+ }
+ & + & {
+ background: amber;
+ }
+}
+
+.foo, .bar {
+ & + & {
+ background: amber;
+ }
+}
+
+.foo, .bar {
+ a, b {
+ & > & {
+ background: amber;
+ }
+ }
+}
+
+.other ::fnord { color: red }
+.other::fnord { color: red }
+.other {
+ ::bnord {color: red }
+ &::bnord {color: red }
+}
+// selector interpolation
+@theme: blood;
+@selector: ~".@{theme}";
+@{selector} {
+ color:red;
+}
+@{selector}red {
+ color: green;
+}
+.red {
+ #@{theme}.@{theme}&.black:@{theme} {
+ color:black;
+ }
+}
+@num: 3;
+:nth-child(@{num}) {
+ selector: interpolated;
+}
+.test {
+ &:nth-child(@{num}) {
+ selector: interpolated;
+ }
+ &:nth-child(odd):not(:nth-child(3)) {
+ color: #ff0000;
+ }
+ }
+[prop],
+[prop=10%],
+[prop="value@{num}"],
+[prop*="val@{num}"],
+[|prop~="val@{num}"],
+[*|prop$="val@{num}"],
+[ns|prop^="val@{num}"],
+[@{num}^="val@{num}"],
+[@{num}=@{num}],
+[@{num}] {
+ attributes: yes;
+}
+
+/*
+Large comment means chunk will be emitted after } which means chunk will begin with whitespace...
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
+*/
+@{selector} {
+ color: red;
+}
+.only-nested {
+ .level2 {
+ .foo:not(.tst&:hover) {
+ test: only-nested;
+ }
+ }
+}
+.nestend-and-non-nested {
+ .foo&:not(.tst&:hover) {
+ test: nestend-and-non-nested;
+ }
+}
+.selector:not(&:hover) {
+ test: global scope;
+}
+// https://github.com/less/less.js/issues/2206
+.extend-this {
+ content: '\2661';
+}
+.first-level {
+ .second-level {
+ .active&:extend(.extend-this) { }
+ &.active2:extend(.extend-this) { }
+ }
+}
--- /dev/null
+@test-var: 'something';
\ No newline at end of file
--- /dev/null
+{
+ "my-color": "red"
+}
\ No newline at end of file
--- /dev/null
+@var: black;
+
+.a() {
+ color: red;
+}
+
+.b {
+ color: green;
+ .a();
+ color: blue;
+ background: @var;
+}
+
+.a, .b {
+ background: green;
+ .c, .d {
+ background: gray;
+ & + & {
+ color: red;
+ }
+ }
+}
+
+.extend:extend(.a all) {
+ color: pink;
+}
+@import (inline) "imported.css";
\ No newline at end of file
--- /dev/null
+/*comments*/\r
+.unused-css {\r
+ color: white;\r
+}\r
+.imported {\r
+ color: black;\r
+}
\ No newline at end of file
--- /dev/null
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
+
+@import "../import/import-and-relative-paths-test";
--- /dev/null
+.units {
+ cancels-to-nothing: (1px / 1px);
+ cancels: ((((10px / 5em) / 1px) * 3em) * 1px);
+}
--- /dev/null
+#strings {
+ background-image: url("http://son-of-a-banana.com");
+ quotes: "~" "~";
+ content: "#*%:&^,)!.(~*})";
+ empty: "";
+ brackets: "{" "}";
+ escapes: "\"hello\" \\world";
+ escapes2: "\"llo";
+}
+#comments {
+ content: "/* hello */ // not-so-secret";
+}
+#single-quote {
+ quotes: "'" "'";
+ content: '""#!&""';
+ empty: '';
+ semi-colon: ';';
+}
+#escaped {
+ filter: ~"DX.Transform.MS.BS.filter(opacity=50)";
+}
+#one-line { image: url(http://tooks.com) }
+#crazy { image: url(http://), "}", url("http://}") }
+#interpolation {
+ @var: '/dev';
+ url: "http://lesscss.org@{var}/image.jpg";
+
+ @var2: 256;
+ url2: "http://lesscss.org/image-@{var2}.jpg";
+
+ @var3: #456;
+ url3: "http://lesscss.org@{var3}";
+
+ @var4: hello;
+ url4: "http://lesscss.org/@{var4}";
+
+ @var5: 54.4px;
+ url5: "http://lesscss.org/@{var5}";
+}
+
+// multiple calls with string interpolation
+
+.mix-mul (@a: green) {
+ color: ~"@{a}";
+}
+.mix-mul-class {
+ .mix-mul(blue);
+ .mix-mul(red);
+ .mix-mul(black);
+ .mix-mul(orange);
+}
+
+@test: Arial, Verdana, San-Serif;
+.watermark {
+ @family: ~"Univers, @{test}";
+ family: @family;
+}
+#iterated-interpolation {
+ @box-small: 10px;
+ @box-large: 100px;
+
+ .mixin { // both ruleset and mixin
+ width: ~"@{box-@{suffix}}";
+ weird: ~"@{box}-@{suffix}}";
+ width-str: "@{box-@{suffix}}";
+ weird-str: "@{box}-@{suffix}}";
+ @box: ~"@{box";
+ @suffix: large;
+ }
+ .interpolation-mixin {
+ .mixin(); //call the above as mixin
+ }
+}
--- /dev/null
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+ background: url("img.jpg") center / 100px;
+ background: #fff url(image.png) center / 1px 100px repeat-x scroll content-box padding-box;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+ background-image: url("http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700");
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
+
+@import "../import/imports/font";
+
+#data-uri {
+ uri: data-uri('image/jpeg;base64', '../../data/image.jpg');
+}
+
+#data-uri-guess {
+ uri: data-uri('../../data/image.jpg');
+}
+
+#data-uri-ascii {
+ uri-1: data-uri('text/html', '../../data/page.html');
+ uri-2: data-uri('../../data/page.html');
+}
+
+#svg-functions {
+ background-image: svg-gradient(to bottom, black, white);
+ background-image: svg-gradient(to bottom, black, orange 3%, white);
+ @green_5: green 5%;
+ @orange_percentage: 3%;
+ @orange_color: orange;
+ background-image: svg-gradient(to bottom, (mix(black, white) + #444) 1%, @orange_color @orange_percentage, ((@green_5)), white 95%);
+}
+
+#data-uri-with-spaces {
+ background-image: url( data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url( ' data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==');
+}
--- /dev/null
+@import "nested-gradient-with-svg-gradient/mixin-consumer.less";
+
+@font-face {
+ src: url("/fonts/garamond-pro.ttf");
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+ not-a-comment: url(//z);
+}
+#shorthands {
+ background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
+ background: url("img.jpg") center / 100px;
+ background: #fff url(image.png) center / 1px 100px repeat-x scroll content-box padding-box;
+}
+#misc {
+ background-image: url(images/image.jpg);
+}
+#data-uri {
+ background: url(data:image/png;charset=utf-8;base64,
+ kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
+ k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
+ kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
+ background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
+ background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700);
+ background-image: url("http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700");
+}
+
+#svg-data-uri {
+ background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
+}
+
+.comma-delimited {
+ background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
+}
+.values {
+ @a: 'Trebuchet';
+ url: url(@a);
+}
+
+@import "import/import-and-relative-paths-test";
+
+#relative-url-import {
+ .unquoted-relative-path-bg;
+ .quoted-relative-path-border-image;
+}
+
+#data-uri {
+ uri: data-uri('image/jpeg;base64', '../data/image.jpg');
+ @var: replace('../data/replace.jpg', "replace", "image");
+ background-image: data-uri(@var), data-uri(replace('../data/image.filext', "filext", "jpg"));
+
+ uri-fragment: data-uri('image/jpeg;base64', '../data/image.jpg#fragment');
+}
+
+#data-uri-guess {
+ uri: data-uri('../data/image.jpg');
+}
+
+#data-uri-ascii {
+ uri-1: data-uri('text/html', '../data/page.html');
+ uri-2: data-uri('../data/page.html');
+}
+
+#file-functions {
+ uri: data-uri('../data/data-uri-fail.png');
+ svg-not-base-64: data-uri('../data/image.svg');
+ size: image-size('../data/data-uri-fail.png');
+ width: image-width('../data/data-uri-fail.png');
+ height: image-height('../data/data-uri-fail.png');
+}
+.add_an_import(@file_to_import) {
+@import "@{file_to_import}";
+}
+
+.add_an_import("file.css");
+
+#svg-functions {
+ background-image: svg-gradient(to bottom, black, white);
+ background-image: svg-gradient(to bottom, black, orange 3%, white);
+ @green_5: green 5%;
+ @orange_percentage: 3%;
+ @orange_color: orange;
+ background-image: svg-gradient(to bottom, (mix(black, white) + #444) 1%, @orange_color @orange_percentage, ((@green_5)), white 95%);
+}
+//it should work also inside @font-face #2035
+@font-face {
+ font-family: 'MyWebFont';
+ src: url(webfont.eot);
+ src+: url('webfont.eot?#iefix');
+ src+_: format('embedded-opentype');
+ src+: url('webfont.woff') format('woff');
+ src+: format('truetype');
+ src+_: url('webfont.ttf');
+ src+: url('webfont.svg#svgFontName') format('svg');
+}
--- /dev/null
+
+@Eight: 8;
+@charset "UTF-@{Eight}";
+
+@ns: less;
+@namespace @ns "http://lesscss.org";
+
+@name: enlarger;
+@keyframes @name {
+ from {font-size: 12px;}
+ to {font-size: 15px;}
+}
+
+.m(reducer);
+.m(@name) {
+ @-webkit-keyframes @name {
+ from {font-size: 13px;}
+ to {font-size: 10px;}
+ }
+}
--- /dev/null
+@a: 2;
+@x: (@a * @a);
+@y: (@x + 1);
+@z: (@x * 2 + @y);
+@var: -1;
+
+.variables {
+ width: (@z + 1cm); // 14cm
+}
+
+@b: @a * 10;
+@c: #888;
+
+@fonts: "Trebuchet MS", Verdana, sans-serif;
+@f: @fonts;
+
+@quotes: "~" "~";
+@q: @quotes;
+@onePixel: 1px;
+
+.variables {
+ height: (@b + @x + 0px); // 24px
+ color: @c;
+ font-family: @f;
+ quotes: @q;
+}
+
+.redef {
+ @var: 0;
+ .inition {
+ @var: 4;
+ @var: 2;
+ three: @var;
+ @var: 3;
+ }
+ zero: @var;
+}
+
+@important-var: @c !important;
+@important-var-two: @a !important;
+.values {
+ minus-one: @var;
+ @a: 'Trebuchet';
+ @multi: 'A', B, C;
+ font-family: @a, @a, @a;
+ color: @c !important;
+ same-color: @important-var;
+ same-again: @important-var !important;
+ multi-important: @important-var @important-var, @important-var-two;
+ multi: something @multi, @a;
+}
+
+.variable-names {
+ .quoted {
+ @var: 'hello';
+ @name: 'var';
+ name: @@name;
+ }
+
+ .unquoted {
+ @var: 'hello';
+ @name: var;
+ name: @@name;
+ }
+
+ .color-keyword {
+ @red: 'hello';
+ @name: red;
+ name: @@name;
+ }
+}
+
+.alpha {
+ @var: 42;
+ filter: alpha(opacity=@var);
+}
+
+.polluteMixin() {
+ @a: 'pollution';
+}
+.testPollution {
+ @a: 'no-pollution';
+ a: @a;
+ .polluteMixin();
+ a: @a;
+}
+
+.units {
+ width: @onePixel;
+ same-unit-as-previously: (@onePixel / @onePixel);
+ square-pixel-divided: (@onePixel * @onePixel / @onePixel);
+ odd-unit: unit((@onePixel * 4em / 2cm));
+ percentage: (10 * 50%);
+ pixels: (50px * 10);
+ conversion-metric-a: (20mm + 1cm);
+ conversion-metric-b: (1cm + 20mm);
+ conversion-imperial: (1in + 72pt + 6pc);
+ custom-unit: (42octocats * 10);
+ custom-unit-cancelling: (8cats * 9dogs / 4cats);
+ mix-units: (1px + 1em);
+ invalid-units: (1px * 1px);
+ .fallback {
+ @px: 14px;
+ @em: 1.4em;
+ @cm: 10cm;
+ div-px-1: (@px / @em);
+ div-px-2: ((@px / @em) / @cm);
+ sub-px-1: (@px - @em);
+ sub-cm-1: (@cm - (@px - @em));
+ mul-px-1: (@px * @em);
+ mul-em-1: (@em * @px);
+ mul-em-2: ((@em * @px) * @cm);
+ mul-cm-1: (@cm * (@em * @px));
+ add-px-1: (@px + @em);
+ add-px-2: ((@px + @em) + @cm);
+ mul-px-2: ((1 * @px) * @cm);
+ mul-px-3: ((@px * 1) * @cm);
+ }
+}
--- /dev/null
+
+
+.whitespace
+ { color: white; }
+
+.whitespace
+{
+ color: white;
+}
+ .whitespace
+{ color: white; }
+
+.whitespace{color:white;}
+.whitespace { color : white ; }
+
+.white,
+.space,
+.mania
+{ color: white; }
+
+.no-semi-column { color: white }
+.no-semi-column {
+ color: white;
+ white-space: pre
+}
+.no-semi-column {border: 2px solid white}
+.newlines {
+ background: the,
+ great,
+ wall;
+ border: 2px
+ solid
+ black;
+}
+.empty {
+
+}
+.sel
+.newline_ws .tab_ws {
+color:
+white;
+background-position: 45
+-23;
+}
--- /dev/null
+var less = require('../lib/less'),
+ fs = require('fs');
+
+var input = fs.readFileSync("./test/less/modifyVars/extended.less", 'utf8');
+var expectedCss = fs.readFileSync('./test/css/modifyVars/extended.css', 'utf8');
+var options = {
+ modifyVars: JSON.parse(fs.readFileSync("./test/less/modifyVars/extended.json", 'utf8'))
+};
+
+less.render(input, options, function (err, result) {
+ if (err) {
+ console.log(err);
+ }
+ if (result.css === expectedCss) {
+ console.log("PASS");
+ } else {
+ console.log("FAIL");
+ }
+});
--- /dev/null
+function initRhinoTest() {
+ process = { title: 'dummy' };
+
+ less.tree.functions.add = function (a, b) {
+ return new(less.tree.Dimension)(a.value + b.value);
+ };
+ less.tree.functions.increment = function (a) {
+ return new(less.tree.Dimension)(a.value + 1);
+ };
+ less.tree.functions._color = function (str) {
+ if (str.value === "evil red") { return new(less.tree.Color)("600"); }
+ };
+}
+
+initRhinoTest();
\ No newline at end of file
--- /dev/null
+{"version":3,"sources":["testweb/sourcemaps/basic.less","testweb/sourcemaps/imported.css"],"names":[],"mappings":"AAMA;EACE,YAAA;EAJA,UAAA;EAWA,iBAAA;EALA,WAAA;EACA,iBAAA;;AAJF,EASE;AATF,EASM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;EAGA,UAAA;;AALN;AAAI;AAUJ;EATE,iBAAA;;AADF,EAEE;AAFE,EAEF;AAFF,EAEM;AAFF,EAEE;AAQN,OARE;AAQF,OARM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAQN,OARE,GAQF,UARE;AAQF,OARE,GAEI,KAFJ;AAQF,OARE,GAQF,UARM;AAQN,OARE,GAEI,KAFA;AAEF,EAFF,GAQF,UARE;AAEE,EAFF,GAQF,UARM;AAQN,OARM,GAQN,UARE;AAQF,OARM,GAEA,KAFJ;AAQF,OARM,GAQN,UARM;AAQN,OARM,GAEA,KAFA;AAEF,EAFE,GAQN,UARE;AAEE,EAFE,GAQN,UARM;EAGA,UAAA;;AAKN;EACE,WAAA;;ACxBF;AACA;AACA;AACA;AACA;AACA;AACA","file":"sourcemaps/basic.css"}
\ No newline at end of file
--- /dev/null
+<html>
+ <link type="text/css" rel="stylesheet" media="all" href="import.css">
+ <link type="text/css" rel="stylesheet" media="all" href="basic.css">
+<head>
+</head>
+<body>
+<div id="import-test">id import-test</div>
+<div id="import">id import-test</div>
+<div class="imported inline">class imported inline</div>
+<div id="mixin">class mixin</div>
+<div class="a">class a</div>
+<div class="b">class b</div>
+<div class="b">class b<div class="c">class c</div></div>
+<div class="a">class a<div class="d">class d</div></div>
+<div class="extend">class extend<div class="c">class c</div></div>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+{"version":3,"sources":["../../less/import.less","../../less/import/import-test-b.less","../../less/import/import-test-e.less","../../less/import/import-test-c.less","../../less/import/deeper/deeper-2/url-import-2.less","../../less/import/import-test-d.css"],"names":[],"mappings":"AA0BA,SAAS;;6DAxBqE;YAGlE,sBAAqC;AAEjD;ECFE,YAAA;EACA,UAAA;EDGA,WAAA;EACA,WAAA;;mBAE0D;EEX5D;IAAO,WAAA;;;ACEP;EACE,UAAA;;AFAF;EACE,YAAA;EACA,UAAA;;AGNF;EACE,UAAA;;mBJe+E;EKhBjF;IAAO,aAAA;;;mBLkBgE;EEjBvE;IAAO,WAAA;;;mBFmB+E;EKpBtF;IAAO,aAAA;;;AL0BP;EEzBA;IAAO,WAAA","file":"..\\import.css"}
\ No newline at end of file
--- /dev/null
+Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
+DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
+
+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.
\ No newline at end of file
--- /dev/null
+# Lo-Dash v2.4.2
+A utility library delivering consistency, [customization](https://lodash.com/custom-builds), [performance](https://lodash.com/benchmarks), & [extras](https://lodash.com/#features).
+
+## Download
+
+Check out our [wiki]([https://github.com/lodash/lodash/wiki/build-differences]) for details over the differences between builds.
+
+* Modern builds perfect for newer browsers/environments:<br>
+[Development](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.js) &
+[Production](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.min.js)
+
+* Compatibility builds for older environment support too:<br>
+[Development](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.compat.js) &
+[Production](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.compat.min.js)
+
+* Underscore builds to use as a drop-in replacement:<br>
+[Development](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.underscore.js) &
+[Production](https://raw.github.com/lodash/lodash/2.4.2/dist/lodash.underscore.min.js)
+
+CDN copies are available on [cdnjs](http://cdnjs.com/libraries/lodash.js/) & [jsDelivr](http://www.jsdelivr.com/#!lodash). For smaller file sizes, create [custom builds](https://lodash.com/custom-builds) with only the features needed.
+
+Love modules? We’ve got you covered with [lodash-amd](https://npmjs.org/package/lodash-amd), [lodash-es6](https://github.com/lodash/lodash-es6), [lodash-node](https://npmjs.org/package/lodash-node), & [npm packages](https://npmjs.org/browse/keyword/lodash-modularized) per method.
+
+## Dive in
+
+There’s plenty of **[documentation](https://lodash.com/docs)**, [unit tests](https://lodash.com/tests), & [benchmarks](https://lodash.com/benchmarks).<br>
+Check out <a href="http://devdocs.io/lodash/">DevDocs</a> as a fast, organized, & searchable interface for our documentation.
+
+The full changelog for this release is available on our [wiki](https://github.com/lodash/lodash/wiki/Changelog).<br>
+A list of upcoming features is available on our [roadmap](https://github.com/lodash/lodash/wiki/Roadmap).
+
+## Installation & usage
+
+In browsers:
+
+```html
+<script src="lodash.js"></script>
+```
+
+Using [`npm`](http://npmjs.org/):
+
+```bash
+npm i --save lodash
+
+{sudo} npm i -g lodash
+npm ln lodash
+```
+
+In [Node.js](http://nodejs.org/) & [Ringo](http://ringojs.org/):
+
+```js
+var _ = require('lodash');
+// or as Underscore
+var _ = require('lodash/dist/lodash.underscore');
+```
+
+**Notes:**
+ * Don’t assign values to [special variable](http://nodejs.org/api/repl.html#repl_repl_features) `_` when in the REPL
+ * If Lo-Dash is installed globally, run [`npm ln lodash`](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) in your project’s root directory *before* requiring it
+
+In [Rhino](http://www.mozilla.org/rhino/):
+
+```js
+load('lodash.js');
+```
+
+In an AMD loader:
+
+```js
+require({
+ 'packages': [
+ { 'name': 'lodash', 'location': 'path/to/lodash', 'main': 'lodash' }
+ ]
+},
+['lodash'], function(_) {
+ console.log(_.VERSION);
+});
+```
+
+## Resources
+
+ * Podcasts
+ - [JavaScript Jabber](http://javascriptjabber.com/079-jsj-lo-dash-with-john-david-dalton/)
+
+ * Posts
+ - [Say “Hello” to Lo-Dash](http://kitcambridge.be/blog/say-hello-to-lo-dash/)
+ - [Custom builds in Lo-Dash 2.0](http://kitcambridge.be/blog/custom-builds-in-lo-dash-2-dot-0/)
+
+ * Videos
+ - [Introduction](https://vimeo.com/44154599)
+ - [Origins](https://vimeo.com/44154600)
+ - [Optimizations & builds](https://vimeo.com/44154601)
+ - [Native method use](https://vimeo.com/48576012)
+ - [Testing](https://vimeo.com/45865290)
+ - [CascadiaJS ’12](http://www.youtube.com/watch?v=dpPy4f_SeEk)
+
+ A list of other community created podcasts, posts, & videos is available on our [wiki](https://github.com/lodash/lodash/wiki/Resources).
+
+## Features
+
+ * AMD loader support ([curl](https://github.com/cujojs/curl), [dojo](http://dojotoolkit.org/), [requirejs](http://requirejs.org/), etc.)
+ * [_(…)](https://lodash.com/docs#_) supports intuitive chaining
+ * [_.at](https://lodash.com/docs#at) for cherry-picking collection values
+ * [_.bindKey](https://lodash.com/docs#bindKey) for binding [*“lazy”*](http://michaux.ca/articles/lazy-function-definition-pattern) defined methods
+ * [_.clone](https://lodash.com/docs#clone) supports shallow cloning of `Date` & `RegExp` objects
+ * [_.cloneDeep](https://lodash.com/docs#cloneDeep) for deep cloning arrays & objects
+ * [_.constant](https://lodash.com/docs#constant) & [_.property](https://lodash.com/docs#property) function generators for composing functions
+ * [_.contains](https://lodash.com/docs#contains) accepts a `fromIndex`
+ * [_.create](https://lodash.com/docs#create) for easier object inheritance
+ * [_.createCallback](https://lodash.com/docs#createCallback) for extending callbacks in methods & mixins
+ * [_.curry](https://lodash.com/docs#curry) for creating [curried](http://hughfdjackson.com/javascript/2013/07/06/why-curry-helps/) functions
+ * [_.debounce](https://lodash.com/docs#debounce) & [_.throttle](https://lodash.com/docs#throttle) accept additional `options` for more control
+ * [_.findIndex](https://lodash.com/docs#findIndex) & [_.findKey](https://lodash.com/docs#findKey) for finding indexes & keys
+ * [_.forEach](https://lodash.com/docs#forEach) is chainable & supports exiting early
+ * [_.forIn](https://lodash.com/docs#forIn) for iterating own & inherited properties
+ * [_.forOwn](https://lodash.com/docs#forOwn) for iterating own properties
+ * [_.isPlainObject](https://lodash.com/docs#isPlainObject) for checking if values are created by `Object`
+ * [_.mapValues](https://lodash.com/docs#mapValues) for [mapping](https://lodash.com/docs#map) values to an object
+ * [_.memoize](https://lodash.com/docs#memoize) exposes the `cache` of memoized functions
+ * [_.merge](https://lodash.com/docs#merge) for a deep [_.extend](https://lodash.com/docs#extend)
+ * [_.noop](https://lodash.com/docs#noop) for function placeholders
+ * [_.now](https://lodash.com/docs#now) as a cross-browser `Date.now` alternative
+ * [_.parseInt](https://lodash.com/docs#parseInt) for consistent behavior
+ * [_.pull](https://lodash.com/docs#pull) & [_.remove](https://lodash.com/docs#remove) for mutating arrays
+ * [_.random](https://lodash.com/docs#random) supports returning floating-point numbers
+ * [_.runInContext](https://lodash.com/docs#runInContext) for easier mocking
+ * [_.sortBy](https://lodash.com/docs#sortBy) supports sorting by multiple properties
+ * [_.support](https://lodash.com/docs#support) for flagging environment features
+ * [_.template](https://lodash.com/docs#template) supports [*“imports”*](https://lodash.com/docs#templateSettings_imports) options & [ES6 template delimiters](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals)
+ * [_.transform](https://lodash.com/docs#transform) as a powerful alternative to [_.reduce](https://lodash.com/docs#reduce) for transforming objects
+ * [_.where](https://lodash.com/docs#where) supports deep object comparisons
+ * [_.xor](https://lodash.com/docs#xor) as a companion to [_.difference](https://lodash.com/docs#difference), [_.intersection](https://lodash.com/docs#intersection), & [_.union](https://lodash.com/docs#union)
+ * [_.zip](https://lodash.com/docs#zip) is capable of unzipping values
+ * [_.omit](https://lodash.com/docs#omit), [_.pick](https://lodash.com/docs#pick), &
+ [more](https://lodash.com/docs "_.assign, _.clone, _.cloneDeep, _.first, _.initial, _.isEqual, _.last, _.merge, _.rest") accept callbacks
+ * [_.contains](https://lodash.com/docs#contains), [_.toArray](https://lodash.com/docs#toArray), &
+ [more](https://lodash.com/docs "_.at, _.countBy, _.every, _.filter, _.find, _.forEach, _.forEachRight, _.groupBy, _.invoke, _.map, _.max, _.min, _.pluck, _.reduce, _.reduceRight, _.reject, _.shuffle, _.size, _.some, _.sortBy, _.where") accept strings
+ * [_.filter](https://lodash.com/docs#filter), [_.map](https://lodash.com/docs#map), &
+ [more](https://lodash.com/docs "_.countBy, _.every, _.find, _.findKey, _.findLast, _.findLastIndex, _.findLastKey, _.first, _.groupBy, _.initial, _.last, _.max, _.min, _.reject, _.rest, _.some, _.sortBy, _.sortedIndex, _.uniq") support *“_.pluck”* & *“_.where”* shorthands
+ * [_.findLast](https://lodash.com/docs#findLast), [_.findLastIndex](https://lodash.com/docs#findLastIndex), &
+ [more](https://lodash.com/docs "_.findLastKey, _.forEachRight, _.forInRight, _.forOwnRight, _.partialRight") right-associative methods
+
+## Support
+
+Tested in Chrome 5~31, Firefox 2~25, IE 6-11, Opera 9.25-17, Safari 3-7, Node.js 0.6.21-0.10.22, Narwhal 0.3.2, PhantomJS 1.9.2, RingoJS 0.9, & Rhino 1.7RC5.
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 (Custom Build) <https://lodash.com/>
+ * Build: `lodash -o ./dist/lodash.compat.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <https://lodash.com/license>
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre ES5 environments */
+ var undefined;
+
+ /** Used to pool arrays and objects used internally */
+ var arrayPool = [],
+ objectPool = [];
+
+ /** Used to generate unique IDs */
+ var idCounter = 0;
+
+ /** Used internally to indicate various things */
+ var indicatorObject = {};
+
+ /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+ var keyPrefix = +new Date + '';
+
+ /** Used as the size when optimizations are enabled for large arrays */
+ var largeArraySize = 75;
+
+ /** Used as the max size of the `arrayPool` and `objectPool` */
+ var maxPoolSize = 40;
+
+ /** Used to detect and test whitespace */
+ var whitespace = (
+ // whitespace
+ ' \t\x0B\f\xA0\ufeff' +
+
+ // line terminators
+ '\n\r\u2028\u2029' +
+
+ // unicode category "Zs" space separators
+ '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+ );
+
+ /** Used to match empty string literals in compiled template source */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+ /**
+ * Used to match ES6 template delimiters
+ * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+ /** Used to match regexp flags from their coerced string values */
+ var reFlags = /\w*$/;
+
+ /** Used to detected named functions */
+ var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+ /** Used to match "interpolate" template delimiters */
+ var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+ /** Used to match leading whitespace and zeros to be removed */
+ var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+ /** Used to ensure capturing order of template delimiters */
+ var reNoMatch = /($^)/;
+
+ /** Used to detect functions containing a `this` reference */
+ var reThis = /\bthis\b/;
+
+ /** Used to match unescaped characters in compiled string literals */
+ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+ /** Used to assign default `context` object properties */
+ var contextProps = [
+ 'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object',
+ 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+ 'parseInt', 'setTimeout'
+ ];
+
+ /** Used to fix the JScript [[DontEnum]] bug */
+ var shadowedProps = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+ ];
+
+ /** Used to make template sourceURLs easier to identify */
+ var templateCounter = 0;
+
+ /** `Object#toString` result shortcuts */
+ var argsClass = '[object Arguments]',
+ arrayClass = '[object Array]',
+ boolClass = '[object Boolean]',
+ dateClass = '[object Date]',
+ errorClass = '[object Error]',
+ funcClass = '[object Function]',
+ numberClass = '[object Number]',
+ objectClass = '[object Object]',
+ regexpClass = '[object RegExp]',
+ stringClass = '[object String]';
+
+ /** Used to identify object classifications that `_.clone` supports */
+ var cloneableClasses = {};
+ cloneableClasses[funcClass] = false;
+ cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+ cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+ cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+ cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+ /** Used as an internal `_.debounce` options object */
+ var debounceOptions = {
+ 'leading': false,
+ 'maxWait': 0,
+ 'trailing': false
+ };
+
+ /** Used as the property descriptor for `__bindData__` */
+ var descriptor = {
+ 'configurable': false,
+ 'enumerable': false,
+ 'value': null,
+ 'writable': false
+ };
+
+ /** Used as the data object for `iteratorTemplate` */
+ var iteratorData = {
+ 'args': '',
+ 'array': null,
+ 'bottom': '',
+ 'firstArg': '',
+ 'init': '',
+ 'keys': null,
+ 'loop': '',
+ 'shadowedProps': null,
+ 'support': null,
+ 'top': '',
+ 'useHas': false
+ };
+
+ /** Used to determine if values are of the language type Object */
+ var objectTypes = {
+ 'boolean': false,
+ 'function': true,
+ 'object': true,
+ 'number': false,
+ 'string': false,
+ 'undefined': false
+ };
+
+ /** Used to escape characters for inclusion in compiled string literals */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ /** Used as a reference to the global object */
+ var root = (objectTypes[typeof window] && window) || this;
+
+ /** Detect free variable `exports` */
+ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+ /** Detect free variable `module` */
+ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+ /** Detect the popular CommonJS extension `module.exports` */
+ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+ /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+ var freeGlobal = objectTypes[typeof global] && global;
+ if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+ root = freeGlobal;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.indexOf` without support for binary searches
+ * or `fromIndex` constraints.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * An implementation of `_.contains` for cache objects that mimics the return
+ * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+ *
+ * @private
+ * @param {Object} cache The cache object to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `0` if `value` is found, else `-1`.
+ */
+ function cacheIndexOf(cache, value) {
+ var type = typeof value;
+ cache = cache.cache;
+
+ if (type == 'boolean' || value == null) {
+ return cache[value] ? 0 : -1;
+ }
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value;
+ cache = (cache = cache[type]) && cache[key];
+
+ return type == 'object'
+ ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+ : (cache ? 0 : -1);
+ }
+
+ /**
+ * Adds a given value to the corresponding cache object.
+ *
+ * @private
+ * @param {*} value The value to add to the cache.
+ */
+ function cachePush(value) {
+ var cache = this.cache,
+ type = typeof value;
+
+ if (type == 'boolean' || value == null) {
+ cache[value] = true;
+ } else {
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value,
+ typeCache = cache[type] || (cache[type] = {});
+
+ if (type == 'object') {
+ (typeCache[key] || (typeCache[key] = [])).push(value);
+ } else {
+ typeCache[key] = true;
+ }
+ }
+ }
+
+ /**
+ * Used by `_.max` and `_.min` as the default callback when a given
+ * collection is a string value.
+ *
+ * @private
+ * @param {string} value The character to inspect.
+ * @returns {number} Returns the code unit of given character.
+ */
+ function charAtCallback(value) {
+ return value.charCodeAt(0);
+ }
+
+ /**
+ * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+ * them in ascending order.
+ *
+ * @private
+ * @param {Object} a The object to compare to `b`.
+ * @param {Object} b The object to compare to `a`.
+ * @returns {number} Returns the sort order indicator of `1` or `-1`.
+ */
+ function compareAscending(a, b) {
+ var ac = a.criteria,
+ bc = b.criteria,
+ index = -1,
+ length = ac.length;
+
+ while (++index < length) {
+ var value = ac[index],
+ other = bc[index];
+
+ if (value !== other) {
+ if (value > other || typeof value == 'undefined') {
+ return 1;
+ }
+ if (value < other || typeof other == 'undefined') {
+ return -1;
+ }
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to return the same value for
+ // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See http://code.google.com/p/v8/issues/detail?id=90
+ return a.index - b.index;
+ }
+
+ /**
+ * Creates a cache object to optimize linear searches of large arrays.
+ *
+ * @private
+ * @param {Array} [array=[]] The array to search.
+ * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+ */
+ function createCache(array) {
+ var index = -1,
+ length = array.length,
+ first = array[0],
+ mid = array[(length / 2) | 0],
+ last = array[length - 1];
+
+ if (first && typeof first == 'object' &&
+ mid && typeof mid == 'object' && last && typeof last == 'object') {
+ return false;
+ }
+ var cache = getObject();
+ cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+ var result = getObject();
+ result.array = array;
+ result.cache = cache;
+ result.push = cachePush;
+
+ while (++index < length) {
+ result.push(array[index]);
+ }
+ return result;
+ }
+
+ /**
+ * Used by `template` to escape characters for inclusion in compiled
+ * string literals.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(match) {
+ return '\\' + stringEscapes[match];
+ }
+
+ /**
+ * Gets an array from the array pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Array} The array from the pool.
+ */
+ function getArray() {
+ return arrayPool.pop() || [];
+ }
+
+ /**
+ * Gets an object from the object pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Object} The object from the pool.
+ */
+ function getObject() {
+ return objectPool.pop() || {
+ 'array': null,
+ 'cache': null,
+ 'criteria': null,
+ 'false': false,
+ 'index': 0,
+ 'null': false,
+ 'number': null,
+ 'object': null,
+ 'push': null,
+ 'string': null,
+ 'true': false,
+ 'undefined': false,
+ 'value': null
+ };
+ }
+
+ /**
+ * Checks if `value` is a DOM node in IE < 9.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`.
+ */
+ function isNode(value) {
+ // IE < 9 presents DOM nodes as `Object` objects except they have `toString`
+ // methods that are `typeof` "string" and still can coerce nodes to strings
+ return typeof value.toString != 'function' && typeof (value + '') == 'string';
+ }
+
+ /**
+ * Releases the given array back to the array pool.
+ *
+ * @private
+ * @param {Array} [array] The array to release.
+ */
+ function releaseArray(array) {
+ array.length = 0;
+ if (arrayPool.length < maxPoolSize) {
+ arrayPool.push(array);
+ }
+ }
+
+ /**
+ * Releases the given object back to the object pool.
+ *
+ * @private
+ * @param {Object} [object] The object to release.
+ */
+ function releaseObject(object) {
+ var cache = object.cache;
+ if (cache) {
+ releaseObject(cache);
+ }
+ object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+ if (objectPool.length < maxPoolSize) {
+ objectPool.push(object);
+ }
+ }
+
+ /**
+ * Slices the `collection` from the `start` index up to, but not including,
+ * the `end` index.
+ *
+ * Note: This function is used instead of `Array#slice` to support node lists
+ * in IE < 9 and to ensure dense arrays are returned.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to slice.
+ * @param {number} start The start index.
+ * @param {number} end The end index.
+ * @returns {Array} Returns the new array.
+ */
+ function slice(array, start, end) {
+ start || (start = 0);
+ if (typeof end == 'undefined') {
+ end = array ? array.length : 0;
+ }
+ var index = -1,
+ length = end - start || 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = array[start + index];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Create a new `lodash` function using the given context object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} [context=root] The context object.
+ * @returns {Function} Returns the `lodash` function.
+ */
+ function runInContext(context) {
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See http://es5.github.io/#x11.1.5.
+ context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+ /** Native constructor references */
+ var Array = context.Array,
+ Boolean = context.Boolean,
+ Date = context.Date,
+ Error = context.Error,
+ Function = context.Function,
+ Math = context.Math,
+ Number = context.Number,
+ Object = context.Object,
+ RegExp = context.RegExp,
+ String = context.String,
+ TypeError = context.TypeError;
+
+ /**
+ * Used for `Array` method references.
+ *
+ * Normally `Array.prototype` would suffice, however, using an array literal
+ * avoids issues in Narwhal.
+ */
+ var arrayRef = [];
+
+ /** Used for native method references */
+ var errorProto = Error.prototype,
+ objectProto = Object.prototype,
+ stringProto = String.prototype;
+
+ /** Used to restore the original `_` reference in `noConflict` */
+ var oldDash = context._;
+
+ /** Used to resolve the internal [[Class]] of values */
+ var toString = objectProto.toString;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(toString)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/toString| for [^\]]+/g, '.*?') + '$'
+ );
+
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ clearTimeout = context.clearTimeout,
+ floor = Math.floor,
+ fnToString = Function.prototype.toString,
+ getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+ hasOwnProperty = objectProto.hasOwnProperty,
+ push = arrayRef.push,
+ propertyIsEnumerable = objectProto.propertyIsEnumerable,
+ setTimeout = context.setTimeout,
+ splice = arrayRef.splice,
+ unshift = arrayRef.unshift;
+
+ /** Used to set meta data on functions */
+ var defineProperty = (function() {
+ // IE 8 only accepts DOM elements
+ try {
+ var o = {},
+ func = isNative(func = Object.defineProperty) && func,
+ result = func(o, o, o) && func;
+ } catch(e) { }
+ return result;
+ }());
+
+ /* Native method shortcuts for methods with the same name as other `lodash` methods */
+ var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+ nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+ nativeIsFinite = context.isFinite,
+ nativeIsNaN = context.isNaN,
+ nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeParseInt = context.parseInt,
+ nativeRandom = Math.random;
+
+ /** Used to lookup a built-in constructor by [[Class]] */
+ var ctorByClass = {};
+ ctorByClass[arrayClass] = Array;
+ ctorByClass[boolClass] = Boolean;
+ ctorByClass[dateClass] = Date;
+ ctorByClass[funcClass] = Function;
+ ctorByClass[objectClass] = Object;
+ ctorByClass[numberClass] = Number;
+ ctorByClass[regexpClass] = RegExp;
+ ctorByClass[stringClass] = String;
+
+ /** Used to avoid iterating non-enumerable properties in IE < 9 */
+ var nonEnumProps = {};
+ nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
+ nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
+ nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true };
+ nonEnumProps[objectClass] = { 'constructor': true };
+
+ (function() {
+ var length = shadowedProps.length;
+ while (length--) {
+ var key = shadowedProps[length];
+ for (var className in nonEnumProps) {
+ if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) {
+ nonEnumProps[className][key] = false;
+ }
+ }
+ }
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps the given value to enable intuitive
+ * method chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+ * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+ * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+ * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+ * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+ * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * and `zip`
+ *
+ * The non-chainable wrapper functions are:
+ * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+ * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+ * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+ * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+ * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+ * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+ * `template`, `unescape`, `uniqueId`, and `value`
+ *
+ * The wrapper functions `first` and `last` return wrapped values when `n` is
+ * provided, otherwise they return unwrapped values.
+ *
+ * Explicit chaining can be enabled by using the `_.chain` method.
+ *
+ * @name _
+ * @constructor
+ * @category Chaining
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(num) {
+ * return num * num;
+ * });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+ return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+ ? value
+ : new lodashWrapper(value);
+ }
+
+ /**
+ * A fast path for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @param {boolean} chainAll A flag to enable chaining for all methods
+ * @returns {Object} Returns a `lodash` instance.
+ */
+ function lodashWrapper(value, chainAll) {
+ this.__chain__ = !!chainAll;
+ this.__wrapped__ = value;
+ }
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
+
+ /**
+ * An object used to flag environments features.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ var support = lodash.support = {};
+
+ (function() {
+ var ctor = function() { this.x = 1; },
+ object = { '0': 1, 'length': 1 },
+ props = [];
+
+ ctor.prototype = { 'valueOf': 1, 'y': 1 };
+ for (var key in new ctor) { props.push(key); }
+ for (key in arguments) { }
+
+ /**
+ * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.argsClass = toString.call(arguments) == argsClass;
+
+ /**
+ * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.argsObject = arguments.constructor == Object && !(arguments instanceof Array);
+
+ /**
+ * Detect if `name` or `message` properties of `Error.prototype` are
+ * enumerable by default. (IE < 9, Safari < 5.1)
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name');
+
+ /**
+ * Detect if `prototype` properties are enumerable by default.
+ *
+ * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
+ * (if the prototype or a property on the prototype has been set)
+ * incorrectly sets a function's `prototype` property [[Enumerable]]
+ * value to `true`.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype');
+
+ /**
+ * Detect if functions can be decompiled by `Function#toString`
+ * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+ /**
+ * Detect if `Function#name` is supported (all but IE).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcNames = typeof Function.name == 'string';
+
+ /**
+ * Detect if `arguments` object indexes are non-enumerable
+ * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nonEnumArgs = key != 0;
+
+ /**
+ * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
+ *
+ * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
+ * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nonEnumShadows = !/valueOf/.test(props);
+
+ /**
+ * Detect if own properties are iterated after inherited properties (all but IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.ownLast = props[0] != 'x';
+
+ /**
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
+ *
+ * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
+ * and `splice()` functions that fail to remove the last element, `value[0]`,
+ * of array-like objects even though the `length` property is set to `0`.
+ * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
+ * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
+
+ /**
+ * Detect lack of support for accessing string characters by index.
+ *
+ * IE < 8 can't access characters by index and IE 8 can only access
+ * characters by index on string literals.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
+
+ /**
+ * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9)
+ * and that the JS engine errors when attempting to coerce an object to
+ * a string without a `toString` function.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + ''));
+ } catch(e) {
+ support.nodeClass = true;
+ }
+ }(1));
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in
+ * embedded Ruby (ERB). Change the following template settings to use alternative
+ * delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': /<%-([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': /<%([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
+
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type string
+ */
+ 'variable': '',
+
+ /**
+ * Used to import variables into the compiled template.
+ *
+ * @memberOf _.templateSettings
+ * @type Object
+ */
+ 'imports': {
+
+ /**
+ * A reference to the `lodash` function.
+ *
+ * @memberOf _.templateSettings.imports
+ * @type Function
+ */
+ '_': lodash
+ }
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The template used to create iterator functions.
+ *
+ * @private
+ * @param {Object} data The data object used to populate the text.
+ * @returns {string} Returns the interpolated text.
+ */
+ var iteratorTemplate = function(obj) {
+
+ var __p = 'var index, iterable = ' +
+ (obj.firstArg) +
+ ', result = ' +
+ (obj.init) +
+ ';\nif (!iterable) return result;\n' +
+ (obj.top) +
+ ';';
+ if (obj.array) {
+ __p += '\nvar length = iterable.length; index = -1;\nif (' +
+ (obj.array) +
+ ') { ';
+ if (support.unindexedChars) {
+ __p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } ';
+ }
+ __p += '\n while (++index < length) {\n ' +
+ (obj.loop) +
+ ';\n }\n}\nelse { ';
+ } else if (support.nonEnumArgs) {
+ __p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' +
+ (obj.loop) +
+ ';\n }\n } else { ';
+ }
+
+ if (support.enumPrototypes) {
+ __p += '\n var skipProto = typeof iterable == \'function\';\n ';
+ }
+
+ if (support.enumErrorProps) {
+ __p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ';
+ }
+
+ var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); }
+
+ if (obj.useHas && obj.keys) {
+ __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n';
+ if (conditions.length) {
+ __p += ' if (' +
+ (conditions.join(' && ')) +
+ ') {\n ';
+ }
+ __p +=
+ (obj.loop) +
+ '; ';
+ if (conditions.length) {
+ __p += '\n }';
+ }
+ __p += '\n } ';
+ } else {
+ __p += '\n for (index in iterable) {\n';
+ if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) {
+ __p += ' if (' +
+ (conditions.join(' && ')) +
+ ') {\n ';
+ }
+ __p +=
+ (obj.loop) +
+ '; ';
+ if (conditions.length) {
+ __p += '\n }';
+ }
+ __p += '\n } ';
+ if (support.nonEnumShadows) {
+ __p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ';
+ for (k = 0; k < 7; k++) {
+ __p += '\n index = \'' +
+ (obj.shadowedProps[k]) +
+ '\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))';
+ if (!obj.useHas) {
+ __p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])';
+ }
+ __p += ') {\n ' +
+ (obj.loop) +
+ ';\n } ';
+ }
+ __p += '\n } ';
+ }
+
+ }
+
+ if (obj.array || support.nonEnumArgs) {
+ __p += '\n}';
+ }
+ __p +=
+ (obj.bottom) +
+ ';\nreturn result';
+
+ return __p
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.bind` that creates the bound function and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new bound function.
+ */
+ function baseBind(bindData) {
+ var func = bindData[0],
+ partialArgs = bindData[2],
+ thisArg = bindData[4];
+
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.io/#x15.3.4.5
+ if (partialArgs) {
+ // avoid `arguments` object deoptimizations by using `slice` instead
+ // of `Array.prototype.slice.call` and not assigning `arguments` to a
+ // variable as a ternary expression
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ // mimic the constructor's `return` behavior
+ // http://es5.github.io/#x13.2.2
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ var thisBinding = baseCreate(func.prototype),
+ result = func.apply(thisBinding, args || arguments);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisArg, args || arguments);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.clone` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+ function baseClone(value, isDeep, callback, stackA, stackB) {
+ if (callback) {
+ var result = callback(value);
+ if (typeof result != 'undefined') {
+ return result;
+ }
+ }
+ // inspect [[Class]]
+ var isObj = isObject(value);
+ if (isObj) {
+ var className = toString.call(value);
+ if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) {
+ return value;
+ }
+ var ctor = ctorByClass[className];
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return new ctor(+value);
+
+ case numberClass:
+ case stringClass:
+ return new ctor(value);
+
+ case regexpClass:
+ result = ctor(value.source, reFlags.exec(value));
+ result.lastIndex = value.lastIndex;
+ return result;
+ }
+ } else {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isDeep) {
+ // check for circular references and return corresponding clone
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == value) {
+ return stackB[length];
+ }
+ }
+ result = isArr ? ctor(value.length) : {};
+ }
+ else {
+ result = isArr ? slice(value) : assign({}, value);
+ }
+ // add array properties assigned by `RegExp#exec`
+ if (isArr) {
+ if (hasOwnProperty.call(value, 'index')) {
+ result.index = value.index;
+ }
+ if (hasOwnProperty.call(value, 'input')) {
+ result.input = value.input;
+ }
+ }
+ // exit for shallow clone
+ if (!isDeep) {
+ return result;
+ }
+ // add the source value to the stack of traversed objects
+ // and associate it with its clone
+ stackA.push(value);
+ stackB.push(result);
+
+ // recursively populate clone (susceptible to call stack limits)
+ (isArr ? baseEach : forOwn)(value, function(objValue, key) {
+ result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+ });
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ function baseCreate(prototype, properties) {
+ return isObject(prototype) ? nativeCreate(prototype) : {};
+ }
+ // fallback for browsers without `Object.create`
+ if (!nativeCreate) {
+ baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || context.Object();
+ };
+ }());
+ }
+
+ /**
+ * The base implementation of `_.createCallback` without support for creating
+ * "_.pluck" or "_.where" style callbacks.
+ *
+ * @private
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ */
+ function baseCreateCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ // exit early for no `thisArg` or already bound by `Function#bind`
+ if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+ return func;
+ }
+ var bindData = func.__bindData__;
+ if (typeof bindData == 'undefined') {
+ if (support.funcNames) {
+ bindData = !func.name;
+ }
+ bindData = bindData || !support.funcDecomp;
+ if (!bindData) {
+ var source = fnToString.call(func);
+ if (!support.funcNames) {
+ bindData = !reFuncName.test(source);
+ }
+ if (!bindData) {
+ // checks if `func` references the `this` keyword and stores the result
+ bindData = reThis.test(source);
+ setBindData(func, bindData);
+ }
+ }
+ }
+ // exit early if there are no `this` references or `func` is bound
+ if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 2: return function(a, b) {
+ return func.call(thisArg, a, b);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ }
+ return bind(func, thisArg);
+ }
+
+ /**
+ * The base implementation of `createWrapper` that creates the wrapper and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new function.
+ */
+ function baseCreateWrapper(bindData) {
+ var func = bindData[0],
+ bitmask = bindData[1],
+ partialArgs = bindData[2],
+ partialRightArgs = bindData[3],
+ thisArg = bindData[4],
+ arity = bindData[5];
+
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ key = func;
+
+ function bound() {
+ var thisBinding = isBind ? thisArg : this;
+ if (partialArgs) {
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ if (partialRightArgs || isCurry) {
+ args || (args = slice(arguments));
+ if (partialRightArgs) {
+ push.apply(args, partialRightArgs);
+ }
+ if (isCurry && args.length < arity) {
+ bitmask |= 16 & ~32;
+ return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+ }
+ }
+ args || (args = arguments);
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (this instanceof bound) {
+ thisBinding = baseCreate(func.prototype);
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.difference` that accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {Array} [values] The array of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+ result = [];
+
+ if (isLarge) {
+ var cache = createCache(values);
+ if (cache) {
+ indexOf = cacheIndexOf;
+ values = cache;
+ } else {
+ isLarge = false;
+ }
+ }
+ while (++index < length) {
+ var value = array[index];
+ if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseObject(values);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` without support for callback
+ * shorthands or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+ * @param {number} [fromIndex=0] The index to start from.
+ * @returns {Array} Returns a new flattened array.
+ */
+ function baseFlatten(array, isShallow, isStrict, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+
+ if (value && typeof value == 'object' && typeof value.length == 'number'
+ && (isArray(value) || isArguments(value))) {
+ // recursively flatten arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ value = baseFlatten(value, isShallow, isStrict);
+ }
+ var valIndex = -1,
+ valLength = value.length,
+ resIndex = result.length;
+
+ result.length += valLength;
+ while (++valIndex < valLength) {
+ result[resIndex++] = value[valIndex];
+ }
+ } else if (!isStrict) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+ * that allows partial "_.where" style comparisons.
+ *
+ * @private
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+ // used to indicate that when comparing objects, `a` has at least the properties of `b`
+ if (callback) {
+ var result = callback(a, b);
+ if (typeof result != 'undefined') {
+ return !!result;
+ }
+ }
+ // exit early for identical values
+ if (a === b) {
+ // treat `+0` vs. `-0` as not equal
+ return a !== 0 || (1 / a == 1 / b);
+ }
+ var type = typeof a,
+ otherType = typeof b;
+
+ // exit early for unlike primitive values
+ if (a === a &&
+ !(a && objectTypes[type]) &&
+ !(b && objectTypes[otherType])) {
+ return false;
+ }
+ // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+ // http://es5.github.io/#x15.3.4.4
+ if (a == null || b == null) {
+ return a === b;
+ }
+ // compare [[Class]] names
+ var className = toString.call(a),
+ otherClass = toString.call(b);
+
+ if (className == argsClass) {
+ className = objectClass;
+ }
+ if (otherClass == argsClass) {
+ otherClass = objectClass;
+ }
+ if (className != otherClass) {
+ return false;
+ }
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ // coerce dates and booleans to numbers, dates to milliseconds and booleans
+ // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+ return +a == +b;
+
+ case numberClass:
+ // treat `NaN` vs. `NaN` as equal
+ return (a != +a)
+ ? b != +b
+ // but treat `+0` vs. `-0` as not equal
+ : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+ case regexpClass:
+ case stringClass:
+ // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+ // treat string primitives and their corresponding object instances as equal
+ return a == String(b);
+ }
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ // unwrap any `lodash` wrapped values
+ var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+ bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+ if (aWrapped || bWrapped) {
+ return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+ }
+ // exit for functions and DOM nodes
+ if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) {
+ return false;
+ }
+ // in older versions of Opera, `arguments` objects have `Array` constructors
+ var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor,
+ ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor;
+
+ // non `Object` object instances with different constructors are not equal
+ if (ctorA != ctorB &&
+ !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+ ('constructor' in a && 'constructor' in b)
+ ) {
+ return false;
+ }
+ }
+ // assume cyclic structures are equal
+ // the algorithm for detecting cyclic structures is adapted from ES 5.1
+ // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
+ }
+ }
+ var size = 0;
+ result = true;
+
+ // add `a` and `b` to the stack of traversed objects
+ stackA.push(a);
+ stackB.push(b);
+
+ // recursively compare objects and arrays (susceptible to call stack limits)
+ if (isArr) {
+ // compare lengths to determine if a deep comparison is necessary
+ length = a.length;
+ size = b.length;
+ result = size == length;
+
+ if (result || isWhere) {
+ // deep compare the contents, ignoring non-numeric properties
+ while (size--) {
+ var index = length,
+ value = b[size];
+
+ if (isWhere) {
+ while (index--) {
+ if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+ // which, in this case, is more costly
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ // count the number of properties.
+ size++;
+ // deep compare each property value.
+ return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+ }
+ });
+
+ if (result && !isWhere) {
+ // ensure both objects have the same number of properties
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ // `size` will be `-1` if `a` has more properties than `b`
+ return (result = --size > -1);
+ }
+ });
+ }
+ }
+ stackA.pop();
+ stackB.pop();
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.merge` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates values with source counterparts.
+ */
+ function baseMerge(object, source, callback, stackA, stackB) {
+ (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+ var found,
+ isArr,
+ result = source,
+ value = object[key];
+
+ if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+ // avoid merging previously merged cyclic sources
+ var stackLength = stackA.length;
+ while (stackLength--) {
+ if ((found = stackA[stackLength] == source)) {
+ value = stackB[stackLength];
+ break;
+ }
+ }
+ if (!found) {
+ var isShallow;
+ if (callback) {
+ result = callback(value, source);
+ if ((isShallow = typeof result != 'undefined')) {
+ value = result;
+ }
+ }
+ if (!isShallow) {
+ value = isArr
+ ? (isArray(value) ? value : [])
+ : (isPlainObject(value) ? value : {});
+ }
+ // add `source` and associated `value` to the stack of traversed objects
+ stackA.push(source);
+ stackB.push(value);
+
+ // recursively merge objects and arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ baseMerge(value, source, callback, stackA, stackB);
+ }
+ }
+ }
+ else {
+ if (callback) {
+ result = callback(value, source);
+ if (typeof result == 'undefined') {
+ result = source;
+ }
+ }
+ if (typeof result != 'undefined') {
+ value = result;
+ }
+ }
+ object[key] = value;
+ });
+ }
+
+ /**
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * The base implementation of `_.uniq` without support for callback shorthands
+ * or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function} [callback] The function called per iteration.
+ * @returns {Array} Returns a duplicate-value-free array.
+ */
+ function baseUniq(array, isSorted, callback) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [];
+
+ var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+ seen = (callback || isLarge) ? getArray() : result;
+
+ if (isLarge) {
+ var cache = createCache(seen);
+ indexOf = cacheIndexOf;
+ seen = cache;
+ }
+ while (++index < length) {
+ var value = array[index],
+ computed = callback ? callback(value, index, array) : value;
+
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : indexOf(seen, computed) < 0
+ ) {
+ if (callback || isLarge) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseArray(seen.array);
+ releaseObject(seen);
+ } else if (callback) {
+ releaseArray(seen);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a function that aggregates a collection, creating an object composed
+ * of keys generated from the results of running each element of the collection
+ * through a callback. The given `setter` function sets the keys and values
+ * of the composed object.
+ *
+ * @private
+ * @param {Function} setter The setter function.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter) {
+ return function(collection, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ setter(result, value, callback(value, index, collection), collection);
+ }
+ } else {
+ baseEach(collection, function(value, key, collection) {
+ setter(result, value, callback(value, key, collection), collection);
+ });
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, either curries or invokes `func`
+ * with an optional `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of method flags to compose.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry`
+ * 8 - `_.curry` (bound)
+ * 16 - `_.partial`
+ * 32 - `_.partialRight`
+ * @param {Array} [partialArgs] An array of arguments to prepend to those
+ * provided to the new function.
+ * @param {Array} [partialRightArgs] An array of arguments to append to those
+ * provided to the new function.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new function.
+ */
+ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ isPartial = bitmask & 16,
+ isPartialRight = bitmask & 32;
+
+ if (!isBindKey && !isFunction(func)) {
+ throw new TypeError;
+ }
+ if (isPartial && !partialArgs.length) {
+ bitmask &= ~16;
+ isPartial = partialArgs = false;
+ }
+ if (isPartialRight && !partialRightArgs.length) {
+ bitmask &= ~32;
+ isPartialRight = partialRightArgs = false;
+ }
+ var bindData = func && func.__bindData__;
+ if (bindData && bindData !== true) {
+ // clone `bindData`
+ bindData = slice(bindData);
+ if (bindData[2]) {
+ bindData[2] = slice(bindData[2]);
+ }
+ if (bindData[3]) {
+ bindData[3] = slice(bindData[3]);
+ }
+ // set `thisBinding` is not previously bound
+ if (isBind && !(bindData[1] & 1)) {
+ bindData[4] = thisArg;
+ }
+ // set if previously bound but not currently (subsequent curried functions)
+ if (!isBind && bindData[1] & 1) {
+ bitmask |= 8;
+ }
+ // set curried arity if not yet set
+ if (isCurry && !(bindData[1] & 4)) {
+ bindData[5] = arity;
+ }
+ // append partial left arguments
+ if (isPartial) {
+ push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+ }
+ // append partial right arguments
+ if (isPartialRight) {
+ unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+ }
+ // merge flags
+ bindData[1] |= bitmask;
+ return createWrapper.apply(null, bindData);
+ }
+ // fast path for `_.bind`
+ var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+ return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+ }
+
+ /**
+ * Creates compiled iteration functions.
+ *
+ * @private
+ * @param {...Object} [options] The compile options object(s).
+ * @param {string} [options.array] Code to determine if the iterable is an array or array-like.
+ * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop.
+ * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration.
+ * @param {string} [options.args] A comma separated string of iteration function arguments.
+ * @param {string} [options.top] Code to execute before the iteration branches.
+ * @param {string} [options.loop] Code to execute in the object loop.
+ * @param {string} [options.bottom] Code to execute after the iteration branches.
+ * @returns {Function} Returns the compiled function.
+ */
+ function createIterator() {
+ // data properties
+ iteratorData.shadowedProps = shadowedProps;
+
+ // iterator options
+ iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = '';
+ iteratorData.init = 'iterable';
+ iteratorData.useHas = true;
+
+ // merge options into a template data object
+ for (var object, index = 0; object = arguments[index]; index++) {
+ for (var key in object) {
+ iteratorData[key] = object[key];
+ }
+ }
+ var args = iteratorData.args;
+ iteratorData.firstArg = /^[^,]+/.exec(args)[0];
+
+ // create the function factory
+ var factory = Function(
+ 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' +
+ 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' +
+ 'objectTypes, nonEnumProps, stringClass, stringProto, toString',
+ 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}'
+ );
+
+ // return the compiled function
+ return factory(
+ baseCreateCallback, errorClass, errorProto, hasOwnProperty,
+ indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto,
+ objectTypes, nonEnumProps, stringClass, stringProto, toString
+ );
+ }
+
+ /**
+ * Used by `escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(match) {
+ return htmlEscapes[match];
+ }
+
+ /**
+ * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+ * customized, this method returns the custom method, otherwise it returns
+ * the `baseIndexOf` function.
+ *
+ * @private
+ * @returns {Function} Returns the "indexOf" function.
+ */
+ function getIndexOf() {
+ var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a native function.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+ */
+ function isNative(value) {
+ return typeof value == 'function' && reNative.test(value);
+ }
+
+ /**
+ * Sets `this` binding data on a given function.
+ *
+ * @private
+ * @param {Function} func The function to set data on.
+ * @param {Array} value The data array to set.
+ */
+ var setBindData = !defineProperty ? noop : function(func, value) {
+ descriptor.value = value;
+ defineProperty(func, '__bindData__', descriptor);
+ descriptor.value = null;
+ };
+
+ /**
+ * A fallback implementation of `isPlainObject` which checks if a given value
+ * is an object created by the `Object` constructor, assuming objects created
+ * by the `Object` constructor have no inherited enumerable properties and that
+ * there are no `Object.prototype` extensions.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ */
+ function shimIsPlainObject(value) {
+ var ctor,
+ result;
+
+ // avoid non Object objects, `arguments` objects, and DOM elements
+ if (!(value && toString.call(value) == objectClass) ||
+ (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) ||
+ (!support.argsClass && isArguments(value)) ||
+ (!support.nodeClass && isNode(value))) {
+ return false;
+ }
+ // IE < 9 iterates inherited properties before own properties. If the first
+ // iterated property is an object's own property then there are no inherited
+ // enumerable properties.
+ if (support.ownLast) {
+ forIn(value, function(value, key, object) {
+ result = hasOwnProperty.call(object, key);
+ return false;
+ });
+ return result !== false;
+ }
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ forIn(value, function(value, key) {
+ result = key;
+ });
+ return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+ }
+
+ /**
+ * Used by `unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} match The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ function unescapeHtmlChar(match) {
+ return htmlUnescapes[match];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if `value` is an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })(1, 2, 3);
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == argsClass || false;
+ }
+ // fallback for browsers that can't detect `arguments` objects by [[Class]]
+ if (!support.argsClass) {
+ isArguments = function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false;
+ };
+ }
+
+ /**
+ * Checks if `value` is an array.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+ * @example
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ */
+ var isArray = nativeIsArray || function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == arrayClass || false;
+ };
+
+ /**
+ * A fallback implementation of `Object.keys` which produces an array of the
+ * given object's own enumerable property names.
+ *
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ */
+ var shimKeys = createIterator({
+ 'args': 'object',
+ 'init': '[]',
+ 'top': 'if (!(objectTypes[typeof object])) return result',
+ 'loop': 'result.push(index)'
+ });
+
+ /**
+ * Creates an array composed of the own enumerable property names of an object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ * @example
+ *
+ * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+ */
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (!isObject(object)) {
+ return [];
+ }
+ if ((support.enumPrototypes && typeof object == 'function') ||
+ (support.nonEnumArgs && object.length && isArguments(object))) {
+ return shimKeys(object);
+ }
+ return nativeKeys(object);
+ };
+
+ /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */
+ var eachIteratorOptions = {
+ 'args': 'collection, callback, thisArg',
+ 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",
+ 'array': "typeof length == 'number'",
+ 'keys': keys,
+ 'loop': 'if (callback(iterable[index], index, collection) === false) return result'
+ };
+
+ /** Reusable iterator options for `assign` and `defaults` */
+ var defaultsIteratorOptions = {
+ 'args': 'object, source, guard',
+ 'top':
+ 'var args = arguments,\n' +
+ ' argsIndex = 0,\n' +
+ " argsLength = typeof guard == 'number' ? 2 : args.length;\n" +
+ 'while (++argsIndex < argsLength) {\n' +
+ ' iterable = args[argsIndex];\n' +
+ ' if (iterable && objectTypes[typeof iterable]) {',
+ 'keys': keys,
+ 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]",
+ 'bottom': ' }\n}'
+ };
+
+ /** Reusable iterator options for `forIn` and `forOwn` */
+ var forOwnIteratorOptions = {
+ 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top,
+ 'array': false
+ };
+
+ /**
+ * Used to convert characters to HTML entities:
+ *
+ * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+ * don't require escaping in HTML and have no special meaning unless they're part
+ * of a tag or an unquoted attribute value.
+ * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+ */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
+
+ /** Used to match HTML entities and HTML characters */
+ var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+ reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+ /**
+ * A function compiled to iterate `arguments` objects, arrays, objects, and
+ * strings consistenly across environments, executing the callback for each
+ * element in the collection. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index|key, collection). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @private
+ * @type Function
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ */
+ var baseEach = createIterator(eachIteratorOptions);
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property assignments of previous
+ * sources. If a callback is provided it will be executed to produce the
+ * assigned values. The callback is bound to `thisArg` and invoked with two
+ * arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @alias extend
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize assigning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+ * // => { 'name': 'fred', 'employer': 'slate' }
+ *
+ * var defaults = _.partialRight(_.assign, function(a, b) {
+ * return typeof a == 'undefined' ? b : a;
+ * });
+ *
+ * var object = { 'name': 'barney' };
+ * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var assign = createIterator(defaultsIteratorOptions, {
+ 'top':
+ defaultsIteratorOptions.top.replace(';',
+ ';\n' +
+ "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" +
+ ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' +
+ "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" +
+ ' callback = args[--argsLength];\n' +
+ '}'
+ ),
+ 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]'
+ });
+
+ /**
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+ * be cloned, otherwise they will be assigned by reference. If a callback
+ * is provided it will be executed to produce the cloned values. If the
+ * callback returns `undefined` cloning will be handled by the method instead.
+ * The callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var shallow = _.clone(characters);
+ * shallow[0] === characters[0];
+ * // => true
+ *
+ * var deep = _.clone(characters, true);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * _.mixin({
+ * 'clone': _.partialRight(_.clone, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * })
+ * });
+ *
+ * var clone = _.clone(document.body);
+ * clone.childNodes.length;
+ * // => 0
+ */
+ function clone(value, isDeep, callback, thisArg) {
+ // allows working with "Collections" methods without using their `index`
+ // and `collection` arguments for `isDeep` and `callback`
+ if (typeof isDeep != 'boolean' && isDeep != null) {
+ thisArg = callback;
+ callback = isDeep;
+ isDeep = false;
+ }
+ return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates a deep clone of `value`. If a callback is provided it will be
+ * executed to produce the cloned values. If the callback returns `undefined`
+ * cloning will be handled by the method instead. The callback is bound to
+ * `thisArg` and invoked with one argument; (value).
+ *
+ * Note: This method is loosely based on the structured clone algorithm. Functions
+ * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+ * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+ * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the deep cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var deep = _.cloneDeep(characters);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'node': element
+ * };
+ *
+ * var clone = _.cloneDeep(view, function(value) {
+ * return _.isElement(value) ? value.cloneNode(true) : undefined;
+ * });
+ *
+ * clone.node == view.node;
+ * // => false
+ */
+ function cloneDeep(value, callback, thisArg) {
+ return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates an object that inherits from the given `prototype` object. If a
+ * `properties` object is provided its own enumerable properties are assigned
+ * to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties ? assign(result, properties) : result;
+ }
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property will be ignored.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param- {Object} [guard] Allows working with `_.reduce` without using its
+ * `key` and `object` arguments as sources.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var object = { 'name': 'barney' };
+ * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var defaults = createIterator(defaultsIteratorOptions);
+
+ /**
+ * This method is like `_.findIndex` except that it returns the key of the
+ * first element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': false },
+ * 'fred': { 'age': 40, 'blocked': true },
+ * 'pebbles': { 'age': 1, 'blocked': false }
+ * };
+ *
+ * _.findKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => 'barney' (property order is not guaranteed across environments)
+ *
+ * // using "_.where" callback shorthand
+ * _.findKey(characters, { 'age': 1 });
+ * // => 'pebbles'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findKey(characters, 'blocked');
+ * // => 'fred'
+ */
+ function findKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * This method is like `_.findKey` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': true },
+ * 'fred': { 'age': 40, 'blocked': false },
+ * 'pebbles': { 'age': 1, 'blocked': true }
+ * };
+ *
+ * _.findLastKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastKey(characters, { 'age': 40 });
+ * // => 'fred'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastKey(characters, 'blocked');
+ * // => 'pebbles'
+ */
+ function findLastKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwnRight(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over own and inherited enumerable properties of an object,
+ * executing the callback for each property. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, key, object). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forIn(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+ */
+ var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, {
+ 'useHas': false
+ });
+
+ /**
+ * This method is like `_.forIn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forInRight(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+ */
+ function forInRight(object, callback, thisArg) {
+ var pairs = [];
+
+ forIn(object, function(value, key) {
+ pairs.push(key, value);
+ });
+
+ var length = pairs.length;
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(pairs[length--], pairs[length], object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Iterates over own enumerable properties of an object, executing the callback
+ * for each property. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, key, object). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+ */
+ var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions);
+
+ /**
+ * This method is like `_.forOwn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+ */
+ function forOwnRight(object, callback, thisArg) {
+ var props = keys(object),
+ length = props.length;
+
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ var key = props[length];
+ if (callback(object[key], key, object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Creates a sorted array of property names of all enumerable properties,
+ * own and inherited, of `object` that have function values.
+ *
+ * @static
+ * @memberOf _
+ * @alias methods
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names that have function values.
+ * @example
+ *
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+ */
+ function functions(object) {
+ var result = [];
+ forIn(object, function(value, key) {
+ if (isFunction(value)) {
+ result.push(key);
+ }
+ });
+ return result.sort();
+ }
+
+ /**
+ * Checks if the specified property name exists as a direct property of `object`,
+ * instead of an inherited property.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to check.
+ * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+ * @example
+ *
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
+ */
+ function has(object, key) {
+ return object ? hasOwnProperty.call(object, key) : false;
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of the given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the created inverted object.
+ * @example
+ *
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
+ */
+ function invert(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ result[object[key]] = key;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a boolean value.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+ * @example
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass || false;
+ }
+
+ /**
+ * Checks if `value` is a date.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ */
+ function isDate(value) {
+ return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+ }
+
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ */
+ function isElement(value) {
+ return value && value.nodeType === 1 || false;
+ }
+
+ /**
+ * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+ * length of `0` and objects with no own enumerable properties are considered
+ * "empty".
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object|string} value The value to inspect.
+ * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({});
+ * // => true
+ *
+ * _.isEmpty('');
+ * // => true
+ */
+ function isEmpty(value) {
+ var result = true;
+ if (!value) {
+ return result;
+ }
+ var className = toString.call(value),
+ length = value.length;
+
+ if ((className == arrayClass || className == stringClass ||
+ (support.argsClass ? className == argsClass : isArguments(value))) ||
+ (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+ return !length;
+ }
+ forOwn(value, function() {
+ return (result = false);
+ });
+ return result;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent to each other. If a callback is provided it will be executed
+ * to compare values. If the callback returns `undefined` comparisons will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (a, b).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var copy = { 'name': 'fred' };
+ *
+ * object == copy;
+ * // => false
+ *
+ * _.isEqual(object, copy);
+ * // => true
+ *
+ * var words = ['hello', 'goodbye'];
+ * var otherWords = ['hi', 'goodbye'];
+ *
+ * _.isEqual(words, otherWords, function(a, b) {
+ * var reGreet = /^(?:hello|hi)$/i,
+ * aGreet = _.isString(a) && reGreet.test(a),
+ * bGreet = _.isString(b) && reGreet.test(b);
+ *
+ * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * });
+ * // => true
+ */
+ function isEqual(a, b, callback, thisArg) {
+ return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+ }
+
+ /**
+ * Checks if `value` is, or can be coerced to, a finite number.
+ *
+ * Note: This is not the same as native `isFinite` which will return true for
+ * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+ }
+
+ /**
+ * Checks if `value` is a function.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ */
+ function isFunction(value) {
+ return typeof value == 'function';
+ }
+ // fallback for older versions of Chrome and Safari
+ if (isFunction(/x/)) {
+ isFunction = function(value) {
+ return typeof value == 'function' && toString.call(value) == funcClass;
+ };
+ }
+
+ /**
+ * Checks if `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.io/#x8
+ // and avoid a V8 bug
+ // http://code.google.com/p/v8/issues/detail?id=2291
+ return !!(value && objectTypes[typeof value]);
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * Note: This is not the same as native `isNaN` which will return `true` for
+ * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // `NaN` as a primitive is the only value that is not equal to itself
+ // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(undefined);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is a number.
+ *
+ * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4 * 5);
+ * // => true
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ value && typeof value == 'object' && toString.call(value) == numberClass || false;
+ }
+
+ /**
+ * Checks if `value` is an object created by the `Object` constructor.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * _.isPlainObject(new Shape);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ */
+ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+ };
+
+ /**
+ * Checks if `value` is a regular expression.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+ * @example
+ *
+ * _.isRegExp(/fred/);
+ * // => true
+ */
+ function isRegExp(value) {
+ return value && objectTypes[typeof value] && toString.call(value) == regexpClass || false;
+ }
+
+ /**
+ * Checks if `value` is a string.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('fred');
+ * // => true
+ */
+ function isString(value) {
+ return typeof value == 'string' ||
+ value && typeof value == 'object' && toString.call(value) == stringClass || false;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Creates an object with the same keys as `object` and values generated by
+ * running each own enumerable property of `object` through the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+ * @example
+ *
+ * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ *
+ * var characters = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.mapValues(characters, 'age');
+ * // => { 'fred': 40, 'pebbles': 1 }
+ */
+ function mapValues(object, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ forOwn(object, function(value, key, object) {
+ result[key] = callback(value, key, object);
+ });
+ return result;
+ }
+
+ /**
+ * Recursively merges own enumerable properties of the source object(s), that
+ * don't resolve to `undefined` into the destination object. Subsequent sources
+ * will overwrite property assignments of previous sources. If a callback is
+ * provided it will be executed to produce the merged values of the destination
+ * and source properties. If the callback returns `undefined` merging will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var names = {
+ * 'characters': [
+ * { 'name': 'barney' },
+ * { 'name': 'fred' }
+ * ]
+ * };
+ *
+ * var ages = {
+ * 'characters': [
+ * { 'age': 36 },
+ * { 'age': 40 }
+ * ]
+ * };
+ *
+ * _.merge(names, ages);
+ * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+ *
+ * var food = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var otherFood = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(food, otherFood, function(a, b) {
+ * return _.isArray(a) ? a.concat(b) : undefined;
+ * });
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+ */
+ function merge(object) {
+ var args = arguments,
+ length = 2;
+
+ if (!isObject(object)) {
+ return object;
+ }
+ // allows working with `_.reduce` and `_.reduceRight` without using
+ // their `index` and `collection` arguments
+ if (typeof args[2] != 'number') {
+ length = args.length;
+ }
+ if (length > 3 && typeof args[length - 2] == 'function') {
+ var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+ } else if (length > 2 && typeof args[length - 1] == 'function') {
+ callback = args[--length];
+ }
+ var sources = slice(arguments, 1, length),
+ index = -1,
+ stackA = getArray(),
+ stackB = getArray();
+
+ while (++index < length) {
+ baseMerge(object, sources[index], callback, stackA, stackB);
+ }
+ releaseArray(stackA);
+ releaseArray(stackB);
+ return object;
+ }
+
+ /**
+ * Creates a shallow clone of `object` excluding the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` omitting the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The properties to omit or the
+ * function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object without the omitted properties.
+ * @example
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+ * // => { 'name': 'fred' }
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function omit(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var props = [];
+ forIn(object, function(value, key) {
+ props.push(key);
+ });
+ props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ result[key] = object[key];
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (!callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates a two dimensional array of an object's key-value pairs,
+ * i.e. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a shallow clone of `object` composed of the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` picking the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The function called per
+ * iteration or property names to pick, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
+ * @example
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+ * // => { 'name': 'fred' }
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function pick(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var index = -1,
+ props = baseFlatten(arguments, true, false, 1),
+ length = isObject(object) ? props.length : 0;
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * An alternative to `_.reduce` this method transforms `object` to a new
+ * `accumulator` object which is the result of running each of its own
+ * enumerable properties through a callback, with each callback execution
+ * potentially mutating the `accumulator` object. The callback is bound to
+ * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+ * Callbacks may exit iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] The custom accumulator value.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+ * num *= num;
+ * if (num % 2) {
+ * return result.push(num) < 3;
+ * }
+ * });
+ * // => [1, 9, 25]
+ *
+ * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function transform(object, callback, accumulator, thisArg) {
+ var isArr = isArray(object);
+ if (accumulator == null) {
+ if (isArr) {
+ accumulator = [];
+ } else {
+ var ctor = object && object.constructor,
+ proto = ctor && ctor.prototype;
+
+ accumulator = baseCreate(proto);
+ }
+ }
+ if (callback) {
+ callback = lodash.createCallback(callback, thisArg, 4);
+ (isArr ? baseEach : forOwn)(object, function(value, index, object) {
+ return callback(accumulator, value, index, object);
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * Creates an array composed of the own enumerable property values of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property values.
+ * @example
+ *
+ * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => [1, 2, 3] (property order is not guaranteed across environments)
+ */
+ function values(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of elements from the specified indexes, or keys, of the
+ * `collection`. Indexes may be specified as individual arguments or as arrays
+ * of indexes.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+ * to retrieve, specified as individual indexes or arrays of indexes.
+ * @returns {Array} Returns a new array of elements corresponding to the
+ * provided indexes.
+ * @example
+ *
+ * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+ * // => ['a', 'c', 'e']
+ *
+ * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+ * // => ['fred', 'pebbles']
+ */
+ function at(collection) {
+ var args = arguments,
+ index = -1,
+ props = baseFlatten(args, true, false, 1),
+ length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+ result = Array(length);
+
+ if (support.unindexedChars && isString(collection)) {
+ collection = collection.split('');
+ }
+ while(++index < length) {
+ result[index] = collection[props[index]];
+ }
+ return result;
+ }
+
+ /**
+ * Checks if a given value is present in a collection using strict equality
+ * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+ * offset from the end of the collection.
+ *
+ * @static
+ * @memberOf _
+ * @alias include
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {*} target The value to check for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+ * @example
+ *
+ * _.contains([1, 2, 3], 1);
+ * // => true
+ *
+ * _.contains([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+ * // => true
+ *
+ * _.contains('pebbles', 'eb');
+ * // => true
+ */
+ function contains(collection, target, fromIndex) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = collection ? collection.length : 0,
+ result = false;
+
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+ if (isArray(collection)) {
+ result = indexOf(collection, target, fromIndex) > -1;
+ } else if (typeof length == 'number') {
+ result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+ } else {
+ baseEach(collection, function(value) {
+ if (++index >= fromIndex) {
+ return !(result = value === target);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through the callback. The corresponding value
+ * of each key is the number of times the key was returned by the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+ });
+
+ /**
+ * Checks if the given callback returns truey value for **all** elements of
+ * a collection. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if all elements passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes']);
+ * // => false
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(characters, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(characters, { 'age': 36 });
+ * // => false
+ */
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ return (result = !!callback(value, index, collection));
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning an array of all elements
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that passed the callback check.
+ * @example
+ *
+ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [2, 4, 6]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.filter(characters, 'blocked');
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ */
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning the first element that
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect, findWhere
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.find(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+ *
+ * // using "_.where" callback shorthand
+ * _.find(characters, { 'age': 1 });
+ * // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(characters, 'blocked');
+ * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+ */
+ function find(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ return value;
+ }
+ }
+ } else {
+ var result;
+ baseEach(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+ }
+
+ /**
+ * This method is like `_.find` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * _.findLast([1, 2, 3, 4], function(num) {
+ * return num % 2 == 1;
+ * });
+ * // => 3
+ */
+ function findLast(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forEachRight(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, executing the callback for each
+ * element. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * Note: As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+ * // => logs each number and returns '1,2,3'
+ *
+ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+ * // => logs each number and returns the object (property order is not guaranteed across environments)
+ */
+ function forEach(collection, callback, thisArg) {
+ if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if (callback(collection[index], index, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ baseEach(collection, callback, thisArg);
+ }
+ return collection;
+ }
+
+ /**
+ * This method is like `_.forEach` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias eachRight
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+ * // => logs each number from right to left and returns '3,2,1'
+ */
+ function forEachRight(collection, callback, thisArg) {
+ var iterable = collection,
+ length = collection ? collection.length : 0;
+
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (isArray(collection)) {
+ while (length--) {
+ if (callback(collection[length], length, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ if (typeof length != 'number') {
+ var props = keys(collection);
+ length = props.length;
+ } else if (support.unindexedChars && isString(collection)) {
+ iterable = collection.split('');
+ }
+ baseEach(collection, function(value, key, collection) {
+ key = props ? props[--length] : --length;
+ return callback(iterable[key], key, collection);
+ });
+ }
+ return collection;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of a collection through the callback. The corresponding value
+ * of each key is an array of the elements responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+ });
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of the collection through the given callback. The corresponding
+ * value of each key is the last element responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var keys = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.indexBy(keys, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ */
+ var indexBy = createAggregator(function(result, value, key) {
+ result[key] = value;
+ });
+
+ /**
+ * Invokes the method named by `methodName` on each element in the `collection`
+ * returning an array of the results of each invoked method. Additional arguments
+ * will be provided to each invoked method. If `methodName` is a function it
+ * will be invoked for, and `this` bound to, each element in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [arg] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of the results of each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ var args = slice(arguments, 2),
+ index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+ });
+ return result;
+ }
+
+ /**
+ * Creates an array of values by running each element in the collection
+ * through the callback. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @example
+ *
+ * _.map([1, 2, 3], function(num) { return num * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+ * // => [3, 6, 9] (property order is not guaranteed across environments)
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ if (isArray(collection)) {
+ while (++index < length) {
+ result[index] = callback(collection[index], index, collection);
+ }
+ } else {
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the maximum value of a collection. If the collection is empty or
+ * falsey `-Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.max(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'fred', 'age': 40 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.max(characters, 'age');
+ * // => { 'name': 'fred', 'age': 40 };
+ */
+ function max(collection, callback, thisArg) {
+ var computed = -Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ baseEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current > computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the minimum value of a collection. If the collection is empty or
+ * falsey `Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.min(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'barney', 'age': 36 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.min(characters, 'age');
+ * // => { 'name': 'barney', 'age': 36 };
+ */
+ function min(collection, callback, thisArg) {
+ var computed = Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ baseEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current < computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the value of a specified property from all elements in the collection.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {string} property The name of the property to pluck.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.pluck(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ var pluck = map;
+
+ /**
+ * Reduces a collection to a value which is the accumulated result of running
+ * each element in the collection through the callback, where each successive
+ * callback execution consumes the return value of the previous execution. If
+ * `accumulator` is not provided the first element of the collection will be
+ * used as the initial `accumulator` value. The callback is bound to `thisArg`
+ * and invoked with four arguments; (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function reduce(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ if (noaccum) {
+ accumulator = collection[++index];
+ }
+ while (++index < length) {
+ accumulator = callback(accumulator, collection[index], index, collection);
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection)
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * This method is like `_.reduce` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var list = [[0, 1], [2, 3], [4, 5]];
+ * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+ forEachRight(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The opposite of `_.filter` this method returns the elements of a
+ * collection that the callback does **not** return truey for.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that failed the callback check.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [1, 3, 5]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.reject(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ *
+ * // using "_.where" callback shorthand
+ * _.reject(characters, { 'age': 36 });
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ */
+ function reject(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ return filter(collection, function(value, index, collection) {
+ return !callback(value, index, collection);
+ });
+ }
+
+ /**
+ * Retrieves a random element or `n` random elements from a collection.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to sample.
+ * @param {number} [n] The number of elements to sample.
+ * @param- {Object} [guard] Allows working with functions like `_.map`
+ * without using their `index` arguments as `n`.
+ * @returns {Array} Returns the random sample(s) of `collection`.
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ *
+ * _.sample([1, 2, 3, 4], 2);
+ * // => [3, 1]
+ */
+ function sample(collection, n, guard) {
+ if (collection && typeof collection.length != 'number') {
+ collection = values(collection);
+ } else if (support.unindexedChars && isString(collection)) {
+ collection = collection.split('');
+ }
+ if (n == null || guard) {
+ return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+ }
+ var result = shuffle(collection);
+ result.length = nativeMin(nativeMax(0, n), result.length);
+ return result;
+ }
+
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(collection) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ var rand = baseRandom(0, ++index);
+ result[index] = result[rand];
+ result[rand] = value;
+ });
+ return result;
+ }
+
+ /**
+ * Gets the size of the `collection` by returning `collection.length` for arrays
+ * and array-like objects or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns `collection.length` or number of own enumerable properties.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return typeof length == 'number' ? length : keys(collection).length;
+ }
+
+ /**
+ * Checks if the callback returns a truey value for **any** element of a
+ * collection. The function returns as soon as it finds a passing value and
+ * does not iterate over the entire collection. The callback is bound to
+ * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if any element passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.some(characters, 'blocked');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.some(characters, { 'age': 1 });
+ * // => false
+ */
+ function some(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if ((result = callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ return !(result = callback(value, index, collection));
+ });
+ }
+ return !!result;
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through the callback. This method
+ * performs a stable sort, that is, it will preserve the original sort order
+ * of equal elements. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an array of property names is provided for `callback` the collection
+ * will be sorted by each property value.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of sorted elements.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+ * // => [3, 1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 26 },
+ * { 'name': 'fred', 'age': 30 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(_.sortBy(characters, 'age'), _.values);
+ * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+ *
+ * // sorting by multiple properties
+ * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+ * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+ */
+ function sortBy(collection, callback, thisArg) {
+ var index = -1,
+ isArr = isArray(callback),
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ if (!isArr) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ forEach(collection, function(value, key, collection) {
+ var object = result[++index] = getObject();
+ if (isArr) {
+ object.criteria = map(callback, function(key) { return value[key]; });
+ } else {
+ (object.criteria = getArray())[0] = callback(value, key, collection);
+ }
+ object.index = index;
+ object.value = value;
+ });
+
+ length = result.length;
+ result.sort(compareAscending);
+ while (length--) {
+ var object = result[length];
+ result[length] = object.value;
+ if (!isArr) {
+ releaseArray(object.criteria);
+ }
+ releaseObject(object);
+ }
+ return result;
+ }
+
+ /**
+ * Converts the `collection` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to convert.
+ * @returns {Array} Returns the new converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+ * // => [2, 3, 4]
+ */
+ function toArray(collection) {
+ if (collection && typeof collection.length == 'number') {
+ return (support.unindexedChars && isString(collection))
+ ? collection.split('')
+ : slice(collection);
+ }
+ return values(collection);
+ }
+
+ /**
+ * Performs a deep comparison of each element in a `collection` to the given
+ * `properties` object, returning an array of all elements that have equivalent
+ * property values.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Object} props The object of property values to filter by.
+ * @returns {Array} Returns a new array of elements that have the given properties.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * _.where(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+ *
+ * _.where(characters, { 'pets': ['dino'] });
+ * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+ */
+ var where = filter;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are all falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all values of the provided arrays using strict
+ * equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {...Array} [values] The arrays of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ * // => [1, 3, 4]
+ */
+ function difference(array) {
+ return baseDifference(array, baseFlatten(arguments, true, true, 1));
+ }
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.findIndex(characters, function(chr) {
+ * return chr.age < 20;
+ * });
+ * // => 2
+ *
+ * // using "_.where" callback shorthand
+ * _.findIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findIndex(characters, 'blocked');
+ * // => 1
+ */
+ function findIndex(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ if (callback(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': true },
+ * { 'name': 'fred', 'age': 40, 'blocked': false },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': true }
+ * ];
+ *
+ * _.findLastIndex(characters, function(chr) {
+ * return chr.age > 30;
+ * });
+ * // => 1
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastIndex(characters, 'blocked');
+ * // => 2
+ */
+ function findLastIndex(array, callback, thisArg) {
+ var length = array ? array.length : 0;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(array[length], length, array)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the first element or first `n` elements of an array. If a callback
+ * is provided elements at the beginning of the array are returned as long
+ * as the callback returns truey. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head, take
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the first element(s) of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.first([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.first(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function first(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = -1;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[0] : undefined;
+ }
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, n), length));
+ }
+
+ /**
+ * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+ * is truey, the array will only be flattened a single level. If a callback
+ * is provided each element of the array is passed through the callback before
+ * flattening. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ *
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.flatten(characters, 'pets');
+ * // => ['hoppy', 'baby puss', 'dino']
+ */
+ function flatten(array, isShallow, callback, thisArg) {
+ // juggle arguments
+ if (typeof isShallow != 'boolean' && isShallow != null) {
+ thisArg = callback;
+ callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+ isShallow = false;
+ }
+ if (callback != null) {
+ array = map(array, callback, thisArg);
+ }
+ return baseFlatten(array, isShallow);
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found using
+ * strict equality for comparisons, i.e. `===`. If the array is already sorted
+ * providing `true` for `fromIndex` will run a faster binary search.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ if (typeof fromIndex == 'number') {
+ var length = array ? array.length : 0;
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+ } else if (fromIndex) {
+ var index = sortedIndex(array, value);
+ return array[index] === value ? index : -1;
+ }
+ return baseIndexOf(array, value, fromIndex);
+ }
+
+ /**
+ * Gets all but the last element or last `n` elements of an array. If a
+ * callback is provided elements at the end of the array are excluded from
+ * the result as long as the callback returns truey. The callback is bound
+ * to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.initial([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.initial([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.initial(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function initial(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : callback || n;
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+ }
+
+ /**
+ * Creates an array of unique values present in all provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of shared values.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection() {
+ var args = [],
+ argsIndex = -1,
+ argsLength = arguments.length,
+ caches = getArray(),
+ indexOf = getIndexOf(),
+ trustIndexOf = indexOf === baseIndexOf,
+ seen = getArray();
+
+ while (++argsIndex < argsLength) {
+ var value = arguments[argsIndex];
+ if (isArray(value) || isArguments(value)) {
+ args.push(value);
+ caches.push(trustIndexOf && value.length >= largeArraySize &&
+ createCache(argsIndex ? args[argsIndex] : seen));
+ }
+ }
+ var array = args[0],
+ index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ outer:
+ while (++index < length) {
+ var cache = caches[0];
+ value = array[index];
+
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+ argsIndex = argsLength;
+ (cache || seen).push(value);
+ while (--argsIndex) {
+ cache = caches[argsIndex];
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ }
+ while (argsLength--) {
+ cache = caches[argsLength];
+ if (cache) {
+ releaseObject(cache);
+ }
+ }
+ releaseArray(caches);
+ releaseArray(seen);
+ return result;
+ }
+
+ /**
+ * Gets the last element or last `n` elements of an array. If a callback is
+ * provided elements at the end of the array are returned as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the last element(s) of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ *
+ * _.last([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.last([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.last(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.last(characters, { 'employer': 'na' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function last(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[length - 1] : undefined;
+ }
+ }
+ return slice(array, nativeMax(0, length - n));
+ }
+
+ /**
+ * Gets the index at which the last occurrence of `value` is found using strict
+ * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+ * as the offset from the end of the collection.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var index = array ? array.length : 0;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all provided values from the given array using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {...*} [value] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3, 1, 2, 3];
+ * _.pull(array, 2, 3);
+ * console.log(array);
+ * // => [1, 1]
+ */
+ function pull(array) {
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = args.length,
+ length = array ? array.length : 0;
+
+ while (++argsIndex < argsLength) {
+ var index = -1,
+ value = args[argsIndex];
+ while (++index < length) {
+ if (array[index] === value) {
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`. If `start` is less than `stop` a
+ * zero-length range is created unless a negative `step` is specified.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns a new range array.
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = typeof step == 'number' ? step : (+step || 1);
+
+ if (end == null) {
+ end = start;
+ start = 0;
+ }
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(0, ceil((end - start) / (step || 1))),
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
+ }
+
+ /**
+ * Removes all elements from an array that the callback returns truey for
+ * and returns an array of removed elements. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4, 5, 6];
+ * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+ *
+ * console.log(array);
+ * // => [1, 3, 5]
+ *
+ * console.log(evens);
+ * // => [2, 4, 6]
+ */
+ function remove(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ var value = array[index];
+ if (callback(value, index, array)) {
+ result.push(value);
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The opposite of `_.initial` this method gets all but the first element or
+ * first `n` elements of an array. If a callback function is provided elements
+ * at the beginning of the array are excluded from the result as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias drop, tail
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.rest([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.rest([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.rest(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.rest(characters, { 'employer': 'slate' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function rest(array, callback, thisArg) {
+ if (typeof callback != 'number' && callback != null) {
+ var n = 0,
+ index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+ }
+ return slice(array, n);
+ }
+
+ /**
+ * Uses a binary search to determine the smallest index at which a value
+ * should be inserted into a given sorted array in order to maintain the sort
+ * order of the array. If a callback is provided it will be executed for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([20, 30, 50], 40);
+ * // => 2
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 2
+ *
+ * var dict = {
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * };
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return dict.wordToNumber[word];
+ * });
+ * // => 2
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return this.wordToNumber[word];
+ * }, dict);
+ * // => 2
+ */
+ function sortedIndex(array, value, callback, thisArg) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ // explicitly reference `identity` for better inlining in Firefox
+ callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+ value = callback(value);
+
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ (callback(array[mid]) < value)
+ ? low = mid + 1
+ : high = mid;
+ }
+ return low;
+ }
+
+ /**
+ * Creates an array of unique values, in order, of the provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of combined values.
+ * @example
+ *
+ * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2, 3, 5, 4]
+ */
+ function union() {
+ return baseUniq(baseFlatten(arguments, true, true));
+ }
+
+ /**
+ * Creates a duplicate-value-free version of an array using strict equality
+ * for comparisons, i.e. `===`. If the array is sorted, providing
+ * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+ * each element of `array` is passed through the callback before uniqueness
+ * is computed. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1, 3, 1]);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 1, 2, 2, 3], true);
+ * // => [1, 2, 3]
+ *
+ * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+ * // => ['A', 'b', 'C']
+ *
+ * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+ * // => [1, 2.5, 3]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, callback, thisArg) {
+ // juggle arguments
+ if (typeof isSorted != 'boolean' && isSorted != null) {
+ thisArg = callback;
+ callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+ isSorted = false;
+ }
+ if (callback != null) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ return baseUniq(array, isSorted, callback);
+ }
+
+ /**
+ * Creates an array excluding all provided values using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to filter.
+ * @param {...*} [value] The values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ return baseDifference(array, slice(arguments, 1));
+ }
+
+ /**
+ * Creates an array that is the symmetric difference of the provided arrays.
+ * See http://en.wikipedia.org/wiki/Symmetric_difference.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of values.
+ * @example
+ *
+ * _.xor([1, 2, 3], [5, 2, 1, 4]);
+ * // => [3, 5, 4]
+ *
+ * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+ * // => [1, 4, 5]
+ */
+ function xor() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var array = arguments[index];
+ if (isArray(array) || isArguments(array)) {
+ var result = result
+ ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+ : array;
+ }
+ }
+ return result || [];
+ }
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the first
+ * elements of the given arrays, the second of which contains the second
+ * elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @alias unzip
+ * @category Arrays
+ * @param {...Array} [array] Arrays to process.
+ * @returns {Array} Returns a new array of grouped elements.
+ * @example
+ *
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ */
+ function zip() {
+ var array = arguments.length > 1 ? arguments : arguments[0],
+ index = -1,
+ length = array ? max(pluck(array, 'length')) : 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = pluck(array, index);
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed from arrays of `keys` and `values`. Provide
+ * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+ * or two arrays, one of `keys` and one of corresponding `values`.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Arrays
+ * @param {Array} keys The array of keys.
+ * @param {Array} [values=[]] The array of values.
+ * @returns {Object} Returns an object composed of the given keys and
+ * corresponding values.
+ * @example
+ *
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ function zipObject(keys, values) {
+ var index = -1,
+ length = keys ? keys.length : 0,
+ result = {};
+
+ if (!values && length && !isArray(keys[0])) {
+ values = [];
+ }
+ while (++index < length) {
+ var key = keys[index];
+ if (values) {
+ result[key] = values[index];
+ } else if (key) {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that executes `func`, with the `this` binding and
+ * arguments of the created function, only after being called `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {number} n The number of times the function must be called before
+ * `func` is executed.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('Done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => logs 'Done saving!', after all saves have completed
+ */
+ function after(n, func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with the `this`
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * provided to the bound function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var func = function(greeting) {
+ * return greeting + ' ' + this.name;
+ * };
+ *
+ * func = _.bind(func, { 'name': 'fred' }, 'hi');
+ * func();
+ * // => 'hi fred'
+ */
+ function bind(func, thisArg) {
+ return arguments.length > 2
+ ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+ : createWrapper(func, 1, null, null, thisArg);
+ }
+
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing
+ * method. Method names may be specified as individual arguments or as arrays
+ * of method names. If no method names are provided all the function properties
+ * of `object` will be bound.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {...string} [methodName] The object method names to
+ * bind, specified as individual method names or arrays of method names.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { console.log('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => logs 'clicked docs', when the button is clicked
+ */
+ function bindAll(object) {
+ var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+ index = -1,
+ length = funcs.length;
+
+ while (++index < length) {
+ var key = funcs[index];
+ object[key] = createWrapper(object[key], 1, null, null, object);
+ }
+ return object;
+ }
+
+ /**
+ * Creates a function that, when called, invokes the method at `object[key]`
+ * and prepends any additional `bindKey` arguments to those provided to the bound
+ * function. This method differs from `_.bind` by allowing bound functions to
+ * reference methods that will be redefined or don't yet exist.
+ * See http://michaux.ca/articles/lazy-function-definition-pattern.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object the method belongs to.
+ * @param {string} key The key of the method.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'name': 'fred',
+ * 'greet': function(greeting) {
+ * return greeting + ' ' + this.name;
+ * }
+ * };
+ *
+ * var func = _.bindKey(object, 'greet', 'hi');
+ * func();
+ * // => 'hi fred'
+ *
+ * object.greet = function(greeting) {
+ * return greeting + 'ya ' + this.name + '!';
+ * };
+ *
+ * func();
+ * // => 'hiya fred!'
+ */
+ function bindKey(object, key) {
+ return arguments.length > 2
+ ? createWrapper(key, 19, slice(arguments, 2), null, object)
+ : createWrapper(key, 3, null, null, object);
+ }
+
+ /**
+ * Creates a function that is the composition of the provided functions,
+ * where each function consumes the return value of the function that follows.
+ * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * Each function is executed with the `this` binding of the composed function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {...Function} [func] Functions to compose.
+ * @returns {Function} Returns the new composed function.
+ * @example
+ *
+ * var realNameMap = {
+ * 'pebbles': 'penelope'
+ * };
+ *
+ * var format = function(name) {
+ * name = realNameMap[name.toLowerCase()] || name;
+ * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+ * };
+ *
+ * var greet = function(formatted) {
+ * return 'Hiya ' + formatted + '!';
+ * };
+ *
+ * var welcome = _.compose(greet, format);
+ * welcome('pebbles');
+ * // => 'Hiya Penelope!'
+ */
+ function compose() {
+ var funcs = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ if (!isFunction(funcs[length])) {
+ throw new TypeError;
+ }
+ }
+ return function() {
+ var args = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ args = [funcs[length].apply(this, args)];
+ }
+ return args[0];
+ };
+ }
+
+ /**
+ * Creates a function which accepts one or more arguments of `func` that when
+ * invoked either executes `func` returning its result, if all `func` arguments
+ * have been provided, or returns a function that accepts one or more of the
+ * remaining `func` arguments, and so on. The arity of `func` can be specified
+ * if `func.length` is not sufficient.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var curried = _.curry(function(a, b, c) {
+ * console.log(a + b + c);
+ * });
+ *
+ * curried(1)(2)(3);
+ * // => 6
+ *
+ * curried(1, 2)(3);
+ * // => 6
+ *
+ * curried(1, 2, 3);
+ * // => 6
+ */
+ function curry(func, arity) {
+ arity = typeof arity == 'number' ? arity : (+arity || func.length);
+ return createWrapper(func, 4, null, null, null, arity);
+ }
+
+ /**
+ * Creates a function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked.
+ * Provide an options object to indicate that `func` should be invoked on
+ * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+ * to the debounced function will return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the debounced function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to delay.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+ * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // avoid costly calculations while the window size is in flux
+ * var lazyLayout = _.debounce(calculateLayout, 150);
+ * jQuery(window).on('resize', lazyLayout);
+ *
+ * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+ * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * });
+ *
+ * // ensure `batchLog` is executed once after 1 second of debounced calls
+ * var source = new EventSource('/stream');
+ * source.addEventListener('message', _.debounce(batchLog, 250, {
+ * 'maxWait': 1000
+ * }, false);
+ */
+ function debounce(func, wait, options) {
+ var args,
+ maxTimeoutId,
+ result,
+ stamp,
+ thisArg,
+ timeoutId,
+ trailingCall,
+ lastCalled = 0,
+ maxWait = false,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ wait = nativeMax(0, wait) || 0;
+ if (options === true) {
+ var leading = true;
+ trailing = false;
+ } else if (isObject(options)) {
+ leading = options.leading;
+ maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ var delayed = function() {
+ var remaining = wait - (now() - stamp);
+ if (remaining <= 0) {
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ var isCalled = trailingCall;
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (isCalled) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ } else {
+ timeoutId = setTimeout(delayed, remaining);
+ }
+ };
+
+ var maxDelayed = function() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (trailing || (maxWait !== wait)) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ };
+
+ return function() {
+ args = arguments;
+ stamp = now();
+ thisArg = this;
+ trailingCall = trailing && (timeoutId || !leading);
+
+ if (maxWait === false) {
+ var leadingCall = leading && !timeoutId;
+ } else {
+ if (!maxTimeoutId && !leading) {
+ lastCalled = stamp;
+ }
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0;
+
+ if (isCalled) {
+ if (maxTimeoutId) {
+ maxTimeoutId = clearTimeout(maxTimeoutId);
+ }
+ lastCalled = stamp;
+ result = func.apply(thisArg, args);
+ }
+ else if (!maxTimeoutId) {
+ maxTimeoutId = setTimeout(maxDelayed, remaining);
+ }
+ }
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
+ timeoutId = setTimeout(delayed, wait);
+ }
+ if (leadingCall) {
+ isCalled = true;
+ result = func.apply(thisArg, args);
+ }
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Defers executing the `func` function until the current call stack has cleared.
+ * Additional arguments will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to defer.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) { console.log(text); }, 'deferred');
+ * // logs 'deferred' after one or more milliseconds
+ */
+ function defer(func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 1);
+ return setTimeout(function() { func.apply(undefined, args); }, 1);
+ }
+
+ /**
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay execution.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) { console.log(text); }, 1000, 'later');
+ * // => logs 'later' after one second
+ */
+ function delay(func, wait) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 2);
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided it will be used to determine the cache key for storing the result
+ * based on the arguments provided to the memoized function. By default, the
+ * first argument provided to the memoized function is used as the cache key.
+ * The `func` is executed with the `this` binding of the memoized function.
+ * The result cache is exposed as the `cache` property on the memoized function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] A function used to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var fibonacci = _.memoize(function(n) {
+ * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ * });
+ *
+ * fibonacci(9)
+ * // => 34
+ *
+ * var data = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // modifying the result cache
+ * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+ * get('pebbles');
+ * // => { 'name': 'pebbles', 'age': 1 }
+ *
+ * get.cache.pebbles.name = 'penelope';
+ * get('pebbles');
+ * // => { 'name': 'penelope', 'age': 1 }
+ */
+ function memoize(func, resolver) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var memoized = function() {
+ var cache = memoized.cache,
+ key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+ return hasOwnProperty.call(cache, key)
+ ? cache[key]
+ : (cache[key] = func.apply(this, arguments));
+ }
+ memoized.cache = {};
+ return memoized;
+ }
+
+ /**
+ * Creates a function that is restricted to execute `func` once. Repeat calls to
+ * the function will return the value of the first call. The `func` is executed
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` executes `createApplication` once
+ */
+ function once(func) {
+ var ran,
+ result;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (ran) {
+ return result;
+ }
+ ran = true;
+ result = func.apply(this, arguments);
+
+ // clear the `func` variable so the function may be garbage collected
+ func = null;
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those provided to the new function. This
+ * method is similar to `_.bind` except it does **not** alter the `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ' ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('fred');
+ * // => 'hi fred'
+ */
+ function partial(func) {
+ return createWrapper(func, 16, slice(arguments, 1));
+ }
+
+ /**
+ * This method is like `_.partial` except that `partial` arguments are
+ * appended to those provided to the new function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+ *
+ * var options = {
+ * 'variable': 'data',
+ * 'imports': { 'jq': $ }
+ * };
+ *
+ * defaultsDeep(options, _.templateSettings);
+ *
+ * options.variable
+ * // => 'data'
+ *
+ * options.imports
+ * // => { '_': _, 'jq': $ }
+ */
+ function partialRight(func) {
+ return createWrapper(func, 32, null, slice(arguments, 1));
+ }
+
+ /**
+ * Creates a function that, when executed, will only call the `func` function
+ * at most once per every `wait` milliseconds. Provide an options object to
+ * indicate that `func` should be invoked on the leading and/or trailing edge
+ * of the `wait` timeout. Subsequent calls to the throttled function will
+ * return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the throttled function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to throttle.
+ * @param {number} wait The number of milliseconds to throttle executions to.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // avoid excessively updating the position while scrolling
+ * var throttled = _.throttle(updatePosition, 100);
+ * jQuery(window).on('scroll', throttled);
+ *
+ * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+ * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+ * 'trailing': false
+ * }));
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ if (options === false) {
+ leading = false;
+ } else if (isObject(options)) {
+ leading = 'leading' in options ? options.leading : leading;
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ debounceOptions.leading = leading;
+ debounceOptions.maxWait = wait;
+ debounceOptions.trailing = trailing;
+
+ return debounce(func, wait, debounceOptions);
+ }
+
+ /**
+ * Creates a function that provides `value` to the wrapper function as its
+ * first argument. Additional arguments provided to the function are appended
+ * to those provided to the wrapper function. The wrapper is executed with
+ * the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {*} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '<p>' + func(text) + '</p>';
+ * });
+ *
+ * p('Fred, Wilma, & Pebbles');
+ * // => '<p>Fred, Wilma, & Pebbles</p>'
+ */
+ function wrap(value, wrapper) {
+ return createWrapper(wrapper, 16, [value]);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that returns `value`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value The value to return from the new function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var getter = _.constant(object);
+ * getter() === object;
+ * // => true
+ */
+ function constant(value) {
+ return function() {
+ return value;
+ };
+ }
+
+ /**
+ * Produces a callback bound to an optional `thisArg`. If `func` is a property
+ * name the created callback will return the property value for a given element.
+ * If `func` is an object the created callback will return `true` for elements
+ * that contain the equivalent object properties, otherwise it will return `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // wrap to create custom callback shorthands
+ * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+ * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+ * return !match ? func(callback, thisArg) : function(object) {
+ * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+ * };
+ * });
+ *
+ * _.filter(characters, 'age__gt38');
+ * // => [{ 'name': 'fred', 'age': 40 }]
+ */
+ function createCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (func == null || type == 'function') {
+ return baseCreateCallback(func, thisArg, argCount);
+ }
+ // handle "_.pluck" style callback shorthands
+ if (type != 'object') {
+ return property(func);
+ }
+ var props = keys(func),
+ key = props[0],
+ a = func[key];
+
+ // handle "_.where" style callback shorthands
+ if (props.length == 1 && a === a && !isObject(a)) {
+ // fast path the common case of providing an object with a single
+ // property containing a primitive value
+ return function(object) {
+ var b = object[key];
+ return a === b && (a !== 0 || (1 / a == 1 / b));
+ };
+ }
+ return function(object) {
+ var length = props.length,
+ result = false;
+
+ while (length--) {
+ if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+ break;
+ }
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding HTML entities.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles'
+ */
+ function escape(string) {
+ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+ }
+
+ /**
+ * This method returns the first argument provided to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.identity(object) === object;
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
+
+ /**
+ * Adds function properties of a source object to the destination object.
+ * If `object` is a function methods will be added to its prototype as well.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Function|Object} [object=lodash] object The destination object.
+ * @param {Object} source The object of functions to add.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+ * @example
+ *
+ * function capitalize(string) {
+ * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+ * }
+ *
+ * _.mixin({ 'capitalize': capitalize });
+ * _.capitalize('fred');
+ * // => 'Fred'
+ *
+ * _('fred').capitalize().value();
+ * // => 'Fred'
+ *
+ * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+ * _('fred').capitalize();
+ * // => 'Fred'
+ */
+ function mixin(object, source, options) {
+ var chain = true,
+ methodNames = source && functions(source);
+
+ if (!source || (!options && !methodNames.length)) {
+ if (options == null) {
+ options = source;
+ }
+ ctor = lodashWrapper;
+ source = object;
+ object = lodash;
+ methodNames = functions(source);
+ }
+ if (options === false) {
+ chain = false;
+ } else if (isObject(options) && 'chain' in options) {
+ chain = options.chain;
+ }
+ var ctor = object,
+ isFunc = isFunction(ctor);
+
+ forEach(methodNames, function(methodName) {
+ var func = object[methodName] = source[methodName];
+ if (isFunc) {
+ ctor.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ value = this.__wrapped__,
+ args = [value];
+
+ push.apply(args, arguments);
+ var result = func.apply(object, args);
+ if (chain || chainAll) {
+ if (value === result && isObject(result)) {
+ return this;
+ }
+ result = new ctor(result);
+ result.__chain__ = chainAll;
+ }
+ return result;
+ };
+ }
+ });
+ }
+
+ /**
+ * Reverts the '_' variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ context._ = oldDash;
+ return this;
+ }
+
+ /**
+ * A no-operation function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.noop(object) === undefined;
+ * // => true
+ */
+ function noop() {
+ // no operation performed
+ }
+
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var stamp = _.now();
+ * _.defer(function() { console.log(_.now() - stamp); });
+ * // => logs the number of milliseconds it took for the deferred function to be called
+ */
+ var now = isNative(now = Date.now) && now || function() {
+ return new Date().getTime();
+ };
+
+ /**
+ * Converts the given value into an integer of the specified radix.
+ * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+ * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+ *
+ * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+ * implementations. See http://es5.github.io/#E.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} value The value to parse.
+ * @param {number} [radix] The radix used to interpret the value to parse.
+ * @returns {number} Returns the new integer value.
+ * @example
+ *
+ * _.parseInt('08');
+ * // => 8
+ */
+ var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+ // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+ return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+ };
+
+ /**
+ * Creates a "_.pluck" style function, which returns the `key` value of a
+ * given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} key The name of the property to retrieve.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 36 }
+ * ];
+ *
+ * var getName = _.property('name');
+ *
+ * _.map(characters, getName);
+ * // => ['barney', 'fred']
+ *
+ * _.sortBy(characters, getName);
+ * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
+ */
+ function property(key) {
+ return function(object) {
+ return object[key];
+ };
+ }
+
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is provided a number between `0` and the given number will be
+ * returned. If `floating` is truey or either `min` or `max` are floats a
+ * floating-point number will be returned instead of an integer.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} [min=0] The minimum possible value.
+ * @param {number} [max=1] The maximum possible value.
+ * @param {boolean} [floating=false] Specify returning a floating-point number.
+ * @returns {number} Returns a random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(min, max, floating) {
+ var noMin = min == null,
+ noMax = max == null;
+
+ if (floating == null) {
+ if (typeof min == 'boolean' && noMax) {
+ floating = min;
+ min = 1;
+ }
+ else if (!noMax && typeof max == 'boolean') {
+ floating = max;
+ noMax = true;
+ }
+ }
+ if (noMin && noMax) {
+ max = 1;
+ }
+ min = +min || 0;
+ if (noMax) {
+ max = min;
+ min = 0;
+ } else {
+ max = +max || 0;
+ }
+ if (floating || min % 1 || max % 1) {
+ var rand = nativeRandom();
+ return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+ }
+ return baseRandom(min, max);
+ }
+
+ /**
+ * Resolves the value of property `key` on `object`. If `key` is a function
+ * it will be invoked with the `this` binding of `object` and its result returned,
+ * else the property value is returned. If `object` is falsey then `undefined`
+ * is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to resolve.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = {
+ * 'cheese': 'crumpets',
+ * 'stuff': function() {
+ * return 'nonsense';
+ * }
+ * };
+ *
+ * _.result(object, 'cheese');
+ * // => 'crumpets'
+ *
+ * _.result(object, 'stuff');
+ * // => 'nonsense'
+ */
+ function result(object, key) {
+ if (object) {
+ var value = object[key];
+ return isFunction(value) ? object[key]() : value;
+ }
+ }
+
+ /**
+ * A micro-templating method that handles arbitrary delimiters, preserves
+ * whitespace, and correctly escapes quotes within interpolated code.
+ *
+ * Note: In the development build, `_.template` utilizes sourceURLs for easier
+ * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ *
+ * For more information on precompiling templates see:
+ * https://lodash.com/custom-builds
+ *
+ * For more information on Chrome extension sandboxes see:
+ * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} text The template text.
+ * @param {Object} data The data object used to populate the text.
+ * @param {Object} [options] The options object.
+ * @param {RegExp} [options.escape] The "escape" delimiter.
+ * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+ * @param {Object} [options.imports] An object to import into the template as local variables.
+ * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+ * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [variable] The data object variable name.
+ * @returns {Function|string} Returns a compiled function when no `data` object
+ * is given, else it returns the interpolated text.
+ * @example
+ *
+ * // using the "interpolate" delimiter to create a compiled template
+ * var compiled = _.template('hello <%= name %>');
+ * compiled({ 'name': 'fred' });
+ * // => 'hello fred'
+ *
+ * // using the "escape" delimiter to escape HTML in data property values
+ * _.template('<b><%- value %></b>', { 'value': '<script>' });
+ * // => '<b><script></b>'
+ *
+ * // using the "evaluate" delimiter to generate HTML
+ * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+ * _.template('hello ${ name }', { 'name': 'pebbles' });
+ * // => 'hello pebbles'
+ *
+ * // using the internal `print` function in "evaluate" delimiters
+ * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+ * // => 'hello barney!'
+ *
+ * // using a custom template delimiters
+ * _.templateSettings = {
+ * 'interpolate': /{{([\s\S]+?)}}/g
+ * };
+ *
+ * _.template('hello {{ name }}!', { 'name': 'mustache' });
+ * // => 'hello mustache!'
+ *
+ * // using the `imports` option to import jQuery
+ * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the `sourceURL` option to specify a custom sourceURL for the template
+ * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+ * compiled(data);
+ * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+ *
+ * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+ * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+ * compiled.source;
+ * // => function(data) {
+ * var __t, __p = '', __e = _.escape;
+ * __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+ * return __p;
+ * }
+ *
+ * // using the `source` property to inline compiled templates for meaningful
+ * // line numbers in error messages and a stack trace
+ * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+ * var JST = {\
+ * "main": ' + _.template(mainText).source + '\
+ * };\
+ * ');
+ */
+ function template(text, data, options) {
+ // based on John Resig's `tmpl` implementation
+ // http://ejohn.org/blog/javascript-micro-templating/
+ // and Laura Doktorova's doT.js
+ // https://github.com/olado/doT
+ var settings = lodash.templateSettings;
+ text = String(text || '');
+
+ // avoid missing dependencies when `iteratorTemplate` is not defined
+ options = defaults({}, options, settings);
+
+ var imports = defaults({}, options.imports, settings.imports),
+ importsKeys = keys(imports),
+ importsValues = values(imports);
+
+ var isEvaluating,
+ index = 0,
+ interpolate = options.interpolate || reNoMatch,
+ source = "__p += '";
+
+ // compile the regexp to match each delimiter
+ var reDelimiters = RegExp(
+ (options.escape || reNoMatch).source + '|' +
+ interpolate.source + '|' +
+ (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+ (options.evaluate || reNoMatch).source + '|$'
+ , 'g');
+
+ text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+ interpolateValue || (interpolateValue = esTemplateValue);
+
+ // escape characters that cannot be included in string literals
+ source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+ // replace delimiters with snippets
+ if (escapeValue) {
+ source += "' +\n__e(" + escapeValue + ") +\n'";
+ }
+ if (evaluateValue) {
+ isEvaluating = true;
+ source += "';\n" + evaluateValue + ";\n__p += '";
+ }
+ if (interpolateValue) {
+ source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+ }
+ index = offset + match.length;
+
+ // the JS engine embedded in Adobe products requires returning the `match`
+ // string in order to produce the correct `offset` value
+ return match;
+ });
+
+ source += "';\n";
+
+ // if `variable` is not specified, wrap a with-statement around the generated
+ // code to add the data object to the top of the scope chain
+ var variable = options.variable,
+ hasVariable = variable;
+
+ if (!hasVariable) {
+ variable = 'obj';
+ source = 'with (' + variable + ') {\n' + source + '\n}\n';
+ }
+ // cleanup code by stripping empty strings
+ source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+ .replace(reEmptyStringMiddle, '$1')
+ .replace(reEmptyStringTrailing, '$1;');
+
+ // frame code as the function body
+ source = 'function(' + variable + ') {\n' +
+ (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
+ "var __t, __p = '', __e = _.escape" +
+ (isEvaluating
+ ? ', __j = Array.prototype.join;\n' +
+ "function print() { __p += __j.call(arguments, '') }\n"
+ : ';\n'
+ ) +
+ source +
+ 'return __p\n}';
+
+ // Use a sourceURL for easier debugging.
+ // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
+
+ try {
+ var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
+ } catch(e) {
+ e.source = source;
+ throw e;
+ }
+ if (data) {
+ return result(data);
+ }
+ // provide the compiled function's source by its `toString` method, in
+ // supported environments, or the `source` property as a convenience for
+ // inlining compiled templates during the build process
+ result.source = source;
+ return result;
+ }
+
+ /**
+ * Executes the callback `n` times, returning an array of the results
+ * of each callback execution. The callback is bound to `thisArg` and invoked
+ * with one argument; (index).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} n The number of times to execute the callback.
+ * @param {Function} callback The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns an array of the results of each `callback` execution.
+ * @example
+ *
+ * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+ * // => [3, 6, 4]
+ *
+ * _.times(3, function(n) { mage.castSpell(n); });
+ * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+ *
+ * _.times(3, function(n) { this.cast(n); }, mage);
+ * // => also calls `mage.castSpell(n)` three times
+ */
+ function times(n, callback, thisArg) {
+ n = (n = +n) > -1 ? n : 0;
+ var index = -1,
+ result = Array(n);
+
+ callback = baseCreateCallback(callback, thisArg, 1);
+ while (++index < n) {
+ result[index] = callback(index);
+ }
+ return result;
+ }
+
+ /**
+ * The inverse of `_.escape` this method converts the HTML entities
+ * `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding characters.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to unescape.
+ * @returns {string} Returns the unescaped string.
+ * @example
+ *
+ * _.unescape('Fred, Barney & Pebbles');
+ * // => 'Fred, Barney & Pebbles'
+ */
+ function unescape(string) {
+ return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+ }
+
+ /**
+ * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} [prefix] The value to prefix the ID with.
+ * @returns {string} Returns the unique ID.
+ * @example
+ *
+ * _.uniqueId('contact_');
+ * // => 'contact_104'
+ *
+ * _.uniqueId();
+ * // => '105'
+ */
+ function uniqueId(prefix) {
+ var id = ++idCounter;
+ return String(prefix == null ? '' : prefix) + id;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object that wraps the given value with explicit
+ * method chaining enabled.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _.chain(characters)
+ * .sortBy('age')
+ * .map(function(chr) { return chr.name + ' is ' + chr.age; })
+ * .first()
+ * .value();
+ * // => 'pebbles is 1'
+ */
+ function chain(value) {
+ value = new lodashWrapper(value);
+ value.__chain__ = true;
+ return value;
+ }
+
+ /**
+ * Invokes `interceptor` with the `value` as the first argument and then
+ * returns `value`. The purpose of this method is to "tap into" a method
+ * chain in order to perform operations on intermediate results within
+ * the chain.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3, 4])
+ * .tap(function(array) { array.pop(); })
+ * .reverse()
+ * .value();
+ * // => [3, 2, 1]
+ */
+ function tap(value, interceptor) {
+ interceptor(value);
+ return value;
+ }
+
+ /**
+ * Enables explicit method chaining on the wrapper object.
+ *
+ * @name chain
+ * @memberOf _
+ * @category Chaining
+ * @returns {*} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // without explicit chaining
+ * _(characters).first();
+ * // => { 'name': 'barney', 'age': 36 }
+ *
+ * // with explicit chaining
+ * _(characters).chain()
+ * .first()
+ * .pick('age')
+ * .value();
+ * // => { 'age': 36 }
+ */
+ function wrapperChain() {
+ this.__chain__ = true;
+ return this;
+ }
+
+ /**
+ * Produces the `toString` result of the wrapped value.
+ *
+ * @name toString
+ * @memberOf _
+ * @category Chaining
+ * @returns {string} Returns the string result.
+ * @example
+ *
+ * _([1, 2, 3]).toString();
+ * // => '1,2,3'
+ */
+ function wrapperToString() {
+ return String(this.__wrapped__);
+ }
+
+ /**
+ * Extracts the wrapped value.
+ *
+ * @name valueOf
+ * @memberOf _
+ * @alias value
+ * @category Chaining
+ * @returns {*} Returns the wrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).valueOf();
+ * // => [1, 2, 3]
+ */
+ function wrapperValueOf() {
+ return this.__wrapped__;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return wrapped values when chaining
+ lodash.after = after;
+ lodash.assign = assign;
+ lodash.at = at;
+ lodash.bind = bind;
+ lodash.bindAll = bindAll;
+ lodash.bindKey = bindKey;
+ lodash.chain = chain;
+ lodash.compact = compact;
+ lodash.compose = compose;
+ lodash.constant = constant;
+ lodash.countBy = countBy;
+ lodash.create = create;
+ lodash.createCallback = createCallback;
+ lodash.curry = curry;
+ lodash.debounce = debounce;
+ lodash.defaults = defaults;
+ lodash.defer = defer;
+ lodash.delay = delay;
+ lodash.difference = difference;
+ lodash.filter = filter;
+ lodash.flatten = flatten;
+ lodash.forEach = forEach;
+ lodash.forEachRight = forEachRight;
+ lodash.forIn = forIn;
+ lodash.forInRight = forInRight;
+ lodash.forOwn = forOwn;
+ lodash.forOwnRight = forOwnRight;
+ lodash.functions = functions;
+ lodash.groupBy = groupBy;
+ lodash.indexBy = indexBy;
+ lodash.initial = initial;
+ lodash.intersection = intersection;
+ lodash.invert = invert;
+ lodash.invoke = invoke;
+ lodash.keys = keys;
+ lodash.map = map;
+ lodash.mapValues = mapValues;
+ lodash.max = max;
+ lodash.memoize = memoize;
+ lodash.merge = merge;
+ lodash.min = min;
+ lodash.omit = omit;
+ lodash.once = once;
+ lodash.pairs = pairs;
+ lodash.partial = partial;
+ lodash.partialRight = partialRight;
+ lodash.pick = pick;
+ lodash.pluck = pluck;
+ lodash.property = property;
+ lodash.pull = pull;
+ lodash.range = range;
+ lodash.reject = reject;
+ lodash.remove = remove;
+ lodash.rest = rest;
+ lodash.shuffle = shuffle;
+ lodash.sortBy = sortBy;
+ lodash.tap = tap;
+ lodash.throttle = throttle;
+ lodash.times = times;
+ lodash.toArray = toArray;
+ lodash.transform = transform;
+ lodash.union = union;
+ lodash.uniq = uniq;
+ lodash.values = values;
+ lodash.where = where;
+ lodash.without = without;
+ lodash.wrap = wrap;
+ lodash.xor = xor;
+ lodash.zip = zip;
+ lodash.zipObject = zipObject;
+
+ // add aliases
+ lodash.collect = map;
+ lodash.drop = rest;
+ lodash.each = forEach;
+ lodash.eachRight = forEachRight;
+ lodash.extend = assign;
+ lodash.methods = functions;
+ lodash.object = zipObject;
+ lodash.select = filter;
+ lodash.tail = rest;
+ lodash.unique = uniq;
+ lodash.unzip = zip;
+
+ // add functions to `lodash.prototype`
+ mixin(lodash);
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return unwrapped values when chaining
+ lodash.clone = clone;
+ lodash.cloneDeep = cloneDeep;
+ lodash.contains = contains;
+ lodash.escape = escape;
+ lodash.every = every;
+ lodash.find = find;
+ lodash.findIndex = findIndex;
+ lodash.findKey = findKey;
+ lodash.findLast = findLast;
+ lodash.findLastIndex = findLastIndex;
+ lodash.findLastKey = findLastKey;
+ lodash.has = has;
+ lodash.identity = identity;
+ lodash.indexOf = indexOf;
+ lodash.isArguments = isArguments;
+ lodash.isArray = isArray;
+ lodash.isBoolean = isBoolean;
+ lodash.isDate = isDate;
+ lodash.isElement = isElement;
+ lodash.isEmpty = isEmpty;
+ lodash.isEqual = isEqual;
+ lodash.isFinite = isFinite;
+ lodash.isFunction = isFunction;
+ lodash.isNaN = isNaN;
+ lodash.isNull = isNull;
+ lodash.isNumber = isNumber;
+ lodash.isObject = isObject;
+ lodash.isPlainObject = isPlainObject;
+ lodash.isRegExp = isRegExp;
+ lodash.isString = isString;
+ lodash.isUndefined = isUndefined;
+ lodash.lastIndexOf = lastIndexOf;
+ lodash.mixin = mixin;
+ lodash.noConflict = noConflict;
+ lodash.noop = noop;
+ lodash.now = now;
+ lodash.parseInt = parseInt;
+ lodash.random = random;
+ lodash.reduce = reduce;
+ lodash.reduceRight = reduceRight;
+ lodash.result = result;
+ lodash.runInContext = runInContext;
+ lodash.size = size;
+ lodash.some = some;
+ lodash.sortedIndex = sortedIndex;
+ lodash.template = template;
+ lodash.unescape = unescape;
+ lodash.uniqueId = uniqueId;
+
+ // add aliases
+ lodash.all = every;
+ lodash.any = some;
+ lodash.detect = find;
+ lodash.findWhere = find;
+ lodash.foldl = reduce;
+ lodash.foldr = reduceRight;
+ lodash.include = contains;
+ lodash.inject = reduce;
+
+ mixin(function() {
+ var source = {}
+ forOwn(lodash, function(func, methodName) {
+ if (!lodash.prototype[methodName]) {
+ source[methodName] = func;
+ }
+ });
+ return source;
+ }(), false);
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions capable of returning wrapped and unwrapped values when chaining
+ lodash.first = first;
+ lodash.last = last;
+ lodash.sample = sample;
+
+ // add aliases
+ lodash.take = first;
+ lodash.head = first;
+
+ forOwn(lodash, function(func, methodName) {
+ var callbackable = methodName !== 'sample';
+ if (!lodash.prototype[methodName]) {
+ lodash.prototype[methodName]= function(n, guard) {
+ var chainAll = this.__chain__,
+ result = func(this.__wrapped__, n, guard);
+
+ return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
+ ? result
+ : new lodashWrapper(result, chainAll);
+ };
+ }
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf _
+ * @type string
+ */
+ lodash.VERSION = '2.4.2';
+
+ // add "Chaining" functions to the wrapper
+ lodash.prototype.chain = wrapperChain;
+ lodash.prototype.toString = wrapperToString;
+ lodash.prototype.value = wrapperValueOf;
+ lodash.prototype.valueOf = wrapperValueOf;
+
+ // add `Array` functions that return unwrapped values
+ baseEach(['join', 'pop', 'shift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ result = func.apply(this.__wrapped__, arguments);
+
+ return chainAll
+ ? new lodashWrapper(result, chainAll)
+ : result;
+ };
+ });
+
+ // add `Array` functions that return the existing wrapped value
+ baseEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ func.apply(this.__wrapped__, arguments);
+ return this;
+ };
+ });
+
+ // add `Array` functions that return new wrapped values
+ baseEach(['concat', 'slice', 'splice'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
+ };
+ });
+
+ // avoid array-like object bugs with `Array#shift` and `Array#splice`
+ // in IE < 9, Firefox < 10, Narwhal, and RingoJS
+ if (!support.spliceObjects) {
+ baseEach(['pop', 'shift', 'splice'], function(methodName) {
+ var func = arrayRef[methodName],
+ isSplice = methodName == 'splice';
+
+ lodash.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ value = this.__wrapped__,
+ result = func.apply(value, arguments);
+
+ if (value.length === 0) {
+ delete value[0];
+ }
+ return (chainAll || isSplice)
+ ? new lodashWrapper(result, chainAll)
+ : result;
+ };
+ });
+ }
+
+ return lodash;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose Lo-Dash
+ var _ = runInContext();
+
+ // some AMD build optimizers like r.js check for condition patterns like the following:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // Expose Lo-Dash to the global object even when an AMD loader is present in
+ // case Lo-Dash is loaded with a RequireJS shim config.
+ // See http://requirejs.org/docs/api.html#config-shim
+ root._ = _;
+
+ // define as an anonymous module so, through path mapping, it can be
+ // referenced as the "underscore" module
+ define(function() {
+ return _;
+ });
+ }
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ else if (freeExports && freeModule) {
+ // in Node.js or RingoJS
+ if (moduleExports) {
+ (freeModule.exports = _)._ = _;
+ }
+ // in Narwhal or Rhino -require
+ else {
+ freeExports._ = _;
+ }
+ }
+ else {
+ // in a browser or Rhino
+ root._ = _;
+ }
+}.call(this));
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE
+ * Build: `lodash -o ./dist/lodash.compat.js`
+ */
+;(function(){function n(n,r,e){for(var t=(e||0)-1,o=n?n.length:0;++t<o;)if(n[t]===r)return t;return-1}function r(r,e){var t=typeof e;if(r=r.cache,"boolean"==t||null==e)return r[e]?0:-1;"number"!=t&&"string"!=t&&(t="object");var o="number"==t?e:m+e;return r=(r=r[t])&&r[o],"object"==t?r&&n(r,e)>-1?0:-1:r?0:-1}function e(n){var r=this.cache,e=typeof n;if("boolean"==e||null==n)r[n]=!0;else{"number"!=e&&"string"!=e&&(e="object");var t="number"==e?n:m+n,o=r[e]||(r[e]={});"object"==e?(o[t]||(o[t]=[])).push(n):o[t]=!0;
+
+}}function t(n){return n.charCodeAt(0)}function o(n,r){for(var e=n.criteria,t=r.criteria,o=-1,u=e.length;++o<u;){var a=e[o],i=t[o];if(a!==i){if(a>i||"undefined"==typeof a)return 1;if(a<i||"undefined"==typeof i)return-1}}return n.index-r.index}function u(n){var r=-1,t=n.length,o=n[0],u=n[t/2|0],a=n[t-1];if(o&&"object"==typeof o&&u&&"object"==typeof u&&a&&"object"==typeof a)return!1;var i=l();i["false"]=i["null"]=i["true"]=i.undefined=!1;var c=l();for(c.array=n,c.cache=i,c.push=e;++r<t;)c.push(n[r]);
+
+return c}function a(n){return"\\"+Z[n]}function i(){return v.pop()||[]}function l(){return y.pop()||{array:null,cache:null,criteria:null,"false":!1,index:0,"null":!1,number:null,object:null,push:null,string:null,"true":!1,undefined:!1,value:null}}function c(n){return"function"!=typeof n.toString&&"string"==typeof(n+"")}function f(n){n.length=0,v.length<w&&v.push(n)}function s(n){var r=n.cache;r&&s(r),n.array=n.cache=n.criteria=n.object=n.number=n.string=n.value=null,y.length<w&&y.push(n)}function p(n,r,e){
+r||(r=0),"undefined"==typeof e&&(e=n?n.length:0);for(var t=-1,o=e-r||0,u=Array(o<0?0:o);++t<o;)u[t]=n[r+t];return u}function g(e){function v(n){return n&&"object"==typeof n&&!st(n)&&Ke.call(n,"__wrapped__")?n:new y(n)}function y(n,r){this.__chain__=!!r,this.__wrapped__=n}function w(n){function r(){if(t){var n=p(t);Ue.apply(n,arguments)}if(this instanceof r){var u=rn(e.prototype),a=e.apply(u,n||arguments);return Fn(a)?a:u}return e.apply(o,n||arguments)}var e=n[0],t=n[2],o=n[4];return ft(r,n),r}function Z(n,r,e,t,o){
+if(e){var u=e(n);if("undefined"!=typeof u)return u}var a=Fn(n);if(!a)return n;var l=$e.call(n);if(!G[l]||!lt.nodeClass&&c(n))return n;var s=at[l];switch(l){case H:case W:return new s(+n);case K:case V:return new s(n);case M:return u=s(n.source,O.exec(n)),u.lastIndex=n.lastIndex,u}var g=st(n);if(r){var h=!t;t||(t=i()),o||(o=i());for(var v=t.length;v--;)if(t[v]==n)return o[v];u=g?s(n.length):{}}else u=g?p(n):xt({},n);return g&&(Ke.call(n,"index")&&(u.index=n.index),Ke.call(n,"input")&&(u.input=n.input)),
+r?(t.push(n),o.push(u),(g?wt:Ct)(n,function(n,a){u[a]=Z(n,r,e,t,o)}),h&&(f(t),f(o)),u):u}function rn(n,r){return Fn(n)?Xe(n):{}}function en(n,r,e){if("function"!=typeof n)return ue;if("undefined"==typeof r||!("prototype"in n))return n;var t=n.__bindData__;if("undefined"==typeof t&&(lt.funcNames&&(t=!n.name),t=t||!lt.funcDecomp,!t)){var o=qe.call(n);lt.funcNames||(t=!S.test(o)),t||(t=N.test(o),ft(n,t))}if(t===!1||t!==!0&&1&t[1])return n;switch(e){case 1:return function(e){return n.call(r,e)};case 2:
+return function(e,t){return n.call(r,e,t)};case 3:return function(e,t,o){return n.call(r,e,t,o)};case 4:return function(e,t,o,u){return n.call(r,e,t,o,u)}}return qr(n,r)}function tn(n){function r(){var n=l?a:this;if(o){var h=p(o);Ue.apply(h,arguments)}if((u||f)&&(h||(h=p(arguments)),u&&Ue.apply(h,u),f&&h.length<i))return t|=16,tn([e,s?t:-4&t,h,null,a,i]);if(h||(h=arguments),c&&(e=n[g]),this instanceof r){n=rn(e.prototype);var v=e.apply(n,h);return Fn(v)?v:n}return e.apply(n,h)}var e=n[0],t=n[1],o=n[2],u=n[3],a=n[4],i=n[5],l=1&t,c=2&t,f=4&t,s=8&t,g=e;
+
+return ft(r,n),r}function on(e,t){var o=-1,a=yn(),i=e?e.length:0,l=i>=_&&a===n,c=[];if(l){var f=u(t);f?(a=r,t=f):l=!1}for(;++o<i;){var p=e[o];a(t,p)<0&&c.push(p)}return l&&s(t),c}function an(n,r,e,t){for(var o=(t||0)-1,u=n?n.length:0,a=[];++o<u;){var i=n[o];if(i&&"object"==typeof i&&"number"==typeof i.length&&(st(i)||_n(i))){r||(i=an(i,r,e));var l=-1,c=i.length,f=a.length;for(a.length+=c;++l<c;)a[f++]=i[l]}else e||a.push(i)}return a}function ln(n,r,e,t,o,u){if(e){var a=e(n,r);if("undefined"!=typeof a)return!!a;
+
+}if(n===r)return 0!==n||1/n==1/r;var l=typeof n,s=typeof r;if(n===n&&(!n||!Y[l])&&(!r||!Y[s]))return!1;if(null==n||null==r)return n===r;var p=$e.call(n),g=$e.call(r);if(p==F&&(p=U),g==F&&(g=U),p!=g)return!1;switch(p){case H:case W:return+n==+r;case K:return n!=+n?r!=+r:0==n?1/n==1/r:n==+r;case M:case V:return n==Ae(r)}var v=p==B;if(!v){var y=Ke.call(n,"__wrapped__"),b=Ke.call(r,"__wrapped__");if(y||b)return ln(y?n.__wrapped__:n,b?r.__wrapped__:r,e,t,o,u);if(p!=U||!lt.nodeClass&&(c(n)||c(r)))return!1;
+
+var d=!lt.argsObject&&_n(n)?Oe:n.constructor,m=!lt.argsObject&&_n(r)?Oe:r.constructor;if(d!=m&&!($n(d)&&d instanceof d&&$n(m)&&m instanceof m)&&"constructor"in n&&"constructor"in r)return!1}var _=!o;o||(o=i()),u||(u=i());for(var w=o.length;w--;)if(o[w]==n)return u[w]==r;var x=0;if(a=!0,o.push(n),u.push(r),v){if(w=n.length,x=r.length,a=x==w,a||t)for(;x--;){var j=w,k=r[x];if(t)for(;j--&&!(a=ln(n[j],k,e,t,o,u)););else if(!(a=ln(n[x],k,e,t,o,u)))break}}else kt(r,function(r,i,l){return Ke.call(l,i)?(x++,
+a=Ke.call(n,i)&&ln(n[i],r,e,t,o,u)):h}),a&&!t&&kt(n,function(n,r,e){return Ke.call(e,r)?a=--x>-1:h});return o.pop(),u.pop(),_&&(f(o),f(u)),a}function cn(n,r,e,t,o){(st(r)?or:Ct)(r,function(r,u){var a,i,l=r,c=n[u];if(r&&((i=st(r))||Pt(r))){for(var f=t.length;f--;)if(a=t[f]==r){c=o[f];break}if(!a){var s;e&&(l=e(c,r),(s="undefined"!=typeof l)&&(c=l)),s||(c=i?st(c)?c:[]:Pt(c)?c:{}),t.push(r),o.push(c),s||cn(c,r,e,t,o)}}else e&&(l=e(c,r),"undefined"==typeof l&&(l=r)),"undefined"!=typeof l&&(c=l);n[u]=c;
+
+})}function fn(n,r){return n+We(ut()*(r-n+1))}function sn(e,t,o){var a=-1,l=yn(),c=e?e.length:0,p=[],g=!t&&c>=_&&l===n,h=o||g?i():p;if(g){var v=u(h);l=r,h=v}for(;++a<c;){var y=e[a],b=o?o(y,a,e):y;(t?!a||h[h.length-1]!==b:l(h,b)<0)&&((o||g)&&h.push(b),p.push(y))}return g?(f(h.array),s(h)):o&&f(h),p}function pn(n){return function(r,e,t){var o={};if(e=v.createCallback(e,t,3),st(r))for(var u=-1,a=r.length;++u<a;){var i=r[u];n(o,i,e(i,u,r),r)}else wt(r,function(r,t,u){n(o,r,e(r,t,u),u)});return o}}function gn(n,r,e,t,o,u){
+var a=1&r,i=2&r,l=4&r,c=16&r,f=32&r;if(!i&&!$n(n))throw new Ie;c&&!e.length&&(r&=-17,c=e=!1),f&&!t.length&&(r&=-33,f=t=!1);var s=n&&n.__bindData__;if(s&&s!==!0)return s=p(s),s[2]&&(s[2]=p(s[2])),s[3]&&(s[3]=p(s[3])),!a||1&s[1]||(s[4]=o),!a&&1&s[1]&&(r|=8),!l||4&s[1]||(s[5]=u),c&&Ue.apply(s[2]||(s[2]=[]),e),f&&Je.apply(s[3]||(s[3]=[]),t),s[1]|=r,gn.apply(null,s);var g=1==r||17===r?w:tn;return g([n,r,e,t,o,u])}function hn(){X.shadowedProps=D,X.array=X.bottom=X.loop=X.top="",X.init="iterable",X.useHas=!0;
+
+for(var n,r=0;n=arguments[r];r++)for(var e in n)X[e]=n[e];var t=X.args;X.firstArg=/^[^,]+/.exec(t)[0];var o=Ce("baseCreateCallback, errorClass, errorProto, hasOwnProperty, indicatorObject, isArguments, isArray, isString, keys, objectProto, objectTypes, nonEnumProps, stringClass, stringProto, toString","return function("+t+") {\n"+ct(X)+"\n}");return o(en,q,Ne,Ke,d,_n,st,zn,X.keys,Re,Y,it,V,Te,$e)}function vn(n){return bt[n]}function yn(){var r=(r=v.indexOf)===Cr?n:r;return r}function bn(n){return"function"==typeof n&&Fe.test(n);
+
+}function dn(n){var r,e;return!n||$e.call(n)!=U||(r=n.constructor,$n(r)&&!(r instanceof r))||!lt.argsClass&&_n(n)||!lt.nodeClass&&c(n)?!1:lt.ownLast?(kt(n,function(n,r,t){return e=Ke.call(t,r),!1}),e!==!1):(kt(n,function(n,r){e=r}),"undefined"==typeof e||Ke.call(n,e))}function mn(n){return dt[n]}function _n(n){return n&&"object"==typeof n&&"number"==typeof n.length&&$e.call(n)==F||!1}function wn(n,r,e,t){return"boolean"!=typeof r&&null!=r&&(t=e,e=r,r=!1),Z(n,r,"function"==typeof e&&en(e,t,1))}function xn(n,r,e){
+return Z(n,!0,"function"==typeof r&&en(r,e,1))}function jn(n,r){var e=rn(n);return r?xt(e,r):e}function kn(n,r,e){var t;return r=v.createCallback(r,e,3),Ct(n,function(n,e,o){return r(n,e,o)?(t=e,!1):h}),t}function Cn(n,r,e){var t;return r=v.createCallback(r,e,3),En(n,function(n,e,o){return r(n,e,o)?(t=e,!1):h}),t}function Pn(n,r,e){var t=[];kt(n,function(n,r){t.push(r,n)});var o=t.length;for(r=en(r,e,3);o--&&r(t[o--],t[o],n)!==!1;);return n}function En(n,r,e){var t=gt(n),o=t.length;for(r=en(r,e,3);o--;){
+var u=t[o];if(r(n[u],u,n)===!1)break}return n}function On(n){var r=[];return kt(n,function(n,e){$n(n)&&r.push(e)}),r.sort()}function Sn(n,r){return n?Ke.call(n,r):!1}function An(n){for(var r=-1,e=gt(n),t=e.length,o={};++r<t;){var u=e[r];o[n[u]]=u}return o}function In(n){return n===!0||n===!1||n&&"object"==typeof n&&$e.call(n)==H||!1}function Ln(n){return n&&"object"==typeof n&&$e.call(n)==W||!1}function Nn(n){return n&&1===n.nodeType||!1}function Rn(n){var r=!0;if(!n)return r;var e=$e.call(n),t=n.length;
+
+return e==B||e==V||(lt.argsClass?e==F:_n(n))||e==U&&"number"==typeof t&&$n(n.splice)?!t:(Ct(n,function(){return r=!1}),r)}function Tn(n,r,e,t){return ln(n,r,"function"==typeof e&&en(e,t,2))}function Dn(n){return Ze(n)&&!nt(parseFloat(n))}function $n(n){return"function"==typeof n}function Fn(n){return!(!n||!Y[typeof n])}function Bn(n){return Wn(n)&&n!=+n}function Hn(n){return null===n}function Wn(n){return"number"==typeof n||n&&"object"==typeof n&&$e.call(n)==K||!1}function qn(n){return n&&Y[typeof n]&&$e.call(n)==M||!1;
+
+}function zn(n){return"string"==typeof n||n&&"object"==typeof n&&$e.call(n)==V||!1}function Kn(n){return"undefined"==typeof n}function Un(n,r,e){var t={};return r=v.createCallback(r,e,3),Ct(n,function(n,e,o){t[e]=r(n,e,o)}),t}function Mn(n){var r=arguments,e=2;if(!Fn(n))return n;if("number"!=typeof r[2]&&(e=r.length),e>3&&"function"==typeof r[e-2])var t=en(r[--e-1],r[e--],2);else e>2&&"function"==typeof r[e-1]&&(t=r[--e]);for(var o=p(arguments,1,e),u=-1,a=i(),l=i();++u<e;)cn(n,o[u],t,a,l);return f(a),
+f(l),n}function Vn(n,r,e){var t={};if("function"!=typeof r){var o=[];kt(n,function(n,r){o.push(r)}),o=on(o,an(arguments,!0,!1,1));for(var u=-1,a=o.length;++u<a;){var i=o[u];t[i]=n[i]}}else r=v.createCallback(r,e,3),kt(n,function(n,e,o){r(n,e,o)||(t[e]=n)});return t}function Gn(n){for(var r=-1,e=gt(n),t=e.length,o=we(t);++r<t;){var u=e[r];o[r]=[u,n[u]]}return o}function Jn(n,r,e){var t={};if("function"!=typeof r)for(var o=-1,u=an(arguments,!0,!1,1),a=Fn(n)?u.length:0;++o<a;){var i=u[o];i in n&&(t[i]=n[i]);
+
+}else r=v.createCallback(r,e,3),kt(n,function(n,e,o){r(n,e,o)&&(t[e]=n)});return t}function Qn(n,r,e,t){var o=st(n);if(null==e)if(o)e=[];else{var u=n&&n.constructor,a=u&&u.prototype;e=rn(a)}return r&&(r=v.createCallback(r,t,4),(o?wt:Ct)(n,function(n,t,o){return r(e,n,t,o)})),e}function Xn(n){for(var r=-1,e=gt(n),t=e.length,o=we(t);++r<t;)o[r]=n[e[r]];return o}function Yn(n){var r=arguments,e=-1,t=an(r,!0,!1,1),o=r[2]&&r[2][r[1]]===n?1:t.length,u=we(o);for(lt.unindexedChars&&zn(n)&&(n=n.split(""));++e<o;)u[e]=n[t[e]];
+
+return u}function Zn(n,r,e){var t=-1,o=yn(),u=n?n.length:0,a=!1;return e=(e<0?et(0,u+e):e)||0,st(n)?a=o(n,r,e)>-1:"number"==typeof u?a=(zn(n)?n.indexOf(r,e):o(n,r,e))>-1:wt(n,function(n){return++t<e?h:!(a=n===r)}),a}function nr(n,r,e){var t=!0;if(r=v.createCallback(r,e,3),st(n))for(var o=-1,u=n.length;++o<u&&(t=!!r(n[o],o,n)););else wt(n,function(n,e,o){return t=!!r(n,e,o)});return t}function rr(n,r,e){var t=[];if(r=v.createCallback(r,e,3),st(n))for(var o=-1,u=n.length;++o<u;){var a=n[o];r(a,o,n)&&t.push(a);
+
+}else wt(n,function(n,e,o){r(n,e,o)&&t.push(n)});return t}function er(n,r,e){if(r=v.createCallback(r,e,3),!st(n)){var t;return wt(n,function(n,e,o){return r(n,e,o)?(t=n,!1):h}),t}for(var o=-1,u=n.length;++o<u;){var a=n[o];if(r(a,o,n))return a}}function tr(n,r,e){var t;return r=v.createCallback(r,e,3),ur(n,function(n,e,o){return r(n,e,o)?(t=n,!1):h}),t}function or(n,r,e){if(r&&"undefined"==typeof e&&st(n))for(var t=-1,o=n.length;++t<o&&r(n[t],t,n)!==!1;);else wt(n,r,e);return n}function ur(n,r,e){
+var t=n,o=n?n.length:0;if(r=r&&"undefined"==typeof e?r:en(r,e,3),st(n))for(;o--&&r(n[o],o,n)!==!1;);else{if("number"!=typeof o){var u=gt(n);o=u.length}else lt.unindexedChars&&zn(n)&&(t=n.split(""));wt(n,function(n,e,a){return e=u?u[--o]:--o,r(t[e],e,a)})}return n}function ar(n,r){var e=p(arguments,2),t=-1,o="function"==typeof r,u=n?n.length:0,a=we("number"==typeof u?u:0);return or(n,function(n){a[++t]=(o?r:n[r]).apply(n,e)}),a}function ir(n,r,e){var t=-1,o=n?n.length:0,u=we("number"==typeof o?o:0);
+
+if(r=v.createCallback(r,e,3),st(n))for(;++t<o;)u[t]=r(n[t],t,n);else wt(n,function(n,e,o){u[++t]=r(n,e,o)});return u}function lr(n,r,e){var o=-(1/0),u=o;if("function"!=typeof r&&e&&e[r]===n&&(r=null),null==r&&st(n))for(var a=-1,i=n.length;++a<i;){var l=n[a];l>u&&(u=l)}else r=null==r&&zn(n)?t:v.createCallback(r,e,3),wt(n,function(n,e,t){var a=r(n,e,t);a>o&&(o=a,u=n)});return u}function cr(n,r,e){var o=1/0,u=o;if("function"!=typeof r&&e&&e[r]===n&&(r=null),null==r&&st(n))for(var a=-1,i=n.length;++a<i;){
+var l=n[a];l<u&&(u=l)}else r=null==r&&zn(n)?t:v.createCallback(r,e,3),wt(n,function(n,e,t){var a=r(n,e,t);a<o&&(o=a,u=n)});return u}function fr(n,r,e,t){var o=arguments.length<3;if(r=v.createCallback(r,t,4),st(n)){var u=-1,a=n.length;for(o&&(e=n[++u]);++u<a;)e=r(e,n[u],u,n)}else wt(n,function(n,t,u){e=o?(o=!1,n):r(e,n,t,u)});return e}function sr(n,r,e,t){var o=arguments.length<3;return r=v.createCallback(r,t,4),ur(n,function(n,t,u){e=o?(o=!1,n):r(e,n,t,u)}),e}function pr(n,r,e){return r=v.createCallback(r,e,3),
+rr(n,function(n,e,t){return!r(n,e,t)})}function gr(n,r,e){if(n&&"number"!=typeof n.length?n=Xn(n):lt.unindexedChars&&zn(n)&&(n=n.split("")),null==r||e)return n?n[fn(0,n.length-1)]:h;var t=hr(n);return t.length=tt(et(0,r),t.length),t}function hr(n){var r=-1,e=n?n.length:0,t=we("number"==typeof e?e:0);return or(n,function(n){var e=fn(0,++r);t[r]=t[e],t[e]=n}),t}function vr(n){var r=n?n.length:0;return"number"==typeof r?r:gt(n).length}function yr(n,r,e){var t;if(r=v.createCallback(r,e,3),st(n))for(var o=-1,u=n.length;++o<u&&!(t=r(n[o],o,n)););else wt(n,function(n,e,o){
+return!(t=r(n,e,o))});return!!t}function br(n,r,e){var t=-1,u=st(r),a=n?n.length:0,c=we("number"==typeof a?a:0);for(u||(r=v.createCallback(r,e,3)),or(n,function(n,e,o){var a=c[++t]=l();u?a.criteria=ir(r,function(r){return n[r]}):(a.criteria=i())[0]=r(n,e,o),a.index=t,a.value=n}),a=c.length,c.sort(o);a--;){var p=c[a];c[a]=p.value,u||f(p.criteria),s(p)}return c}function dr(n){return n&&"number"==typeof n.length?lt.unindexedChars&&zn(n)?n.split(""):p(n):Xn(n)}function mr(n){for(var r=-1,e=n?n.length:0,t=[];++r<e;){
+var o=n[r];o&&t.push(o)}return t}function _r(n){return on(n,an(arguments,!0,!0,1))}function wr(n,r,e){var t=-1,o=n?n.length:0;for(r=v.createCallback(r,e,3);++t<o;)if(r(n[t],t,n))return t;return-1}function xr(n,r,e){var t=n?n.length:0;for(r=v.createCallback(r,e,3);t--;)if(r(n[t],t,n))return t;return-1}function jr(n,r,e){var t=0,o=n?n.length:0;if("number"!=typeof r&&null!=r){var u=-1;for(r=v.createCallback(r,e,3);++u<o&&r(n[u],u,n);)t++}else if(t=r,null==t||e)return n?n[0]:h;return p(n,0,tt(et(0,t),o));
+
+}function kr(n,r,e,t){return"boolean"!=typeof r&&null!=r&&(t=e,e="function"!=typeof r&&t&&t[r]===n?null:r,r=!1),null!=e&&(n=ir(n,e,t)),an(n,r)}function Cr(r,e,t){if("number"==typeof t){var o=r?r.length:0;t=t<0?et(0,o+t):t||0}else if(t){var u=Rr(r,e);return r[u]===e?u:-1}return n(r,e,t)}function Pr(n,r,e){var t=0,o=n?n.length:0;if("number"!=typeof r&&null!=r){var u=o;for(r=v.createCallback(r,e,3);u--&&r(n[u],u,n);)t++}else t=null==r||e?1:r||t;return p(n,0,tt(et(0,o-t),o))}function Er(){for(var e=[],t=-1,o=arguments.length,a=i(),l=yn(),c=l===n,p=i();++t<o;){
+var g=arguments[t];(st(g)||_n(g))&&(e.push(g),a.push(c&&g.length>=_&&u(t?e[t]:p)))}var h=e[0],v=-1,y=h?h.length:0,b=[];n:for(;++v<y;){var d=a[0];if(g=h[v],(d?r(d,g):l(p,g))<0){for(t=o,(d||p).push(g);--t;)if(d=a[t],(d?r(d,g):l(e[t],g))<0)continue n;b.push(g)}}for(;o--;)d=a[o],d&&s(d);return f(a),f(p),b}function Or(n,r,e){var t=0,o=n?n.length:0;if("number"!=typeof r&&null!=r){var u=o;for(r=v.createCallback(r,e,3);u--&&r(n[u],u,n);)t++}else if(t=r,null==t||e)return n?n[o-1]:h;return p(n,et(0,o-t))}function Sr(n,r,e){
+var t=n?n.length:0;for("number"==typeof e&&(t=(e<0?et(0,t+e):tt(e,t-1))+1);t--;)if(n[t]===r)return t;return-1}function Ar(n){for(var r=arguments,e=0,t=r.length,o=n?n.length:0;++e<t;)for(var u=-1,a=r[e];++u<o;)n[u]===a&&(Ge.call(n,u--,1),o--);return n}function Ir(n,r,e){n=+n||0,e="number"==typeof e?e:+e||1,null==r&&(r=n,n=0);for(var t=-1,o=et(0,Be((r-n)/(e||1))),u=we(o);++t<o;)u[t]=n,n+=e;return u}function Lr(n,r,e){var t=-1,o=n?n.length:0,u=[];for(r=v.createCallback(r,e,3);++t<o;){var a=n[t];r(a,t,n)&&(u.push(a),
+Ge.call(n,t--,1),o--)}return u}function Nr(n,r,e){if("number"!=typeof r&&null!=r){var t=0,o=-1,u=n?n.length:0;for(r=v.createCallback(r,e,3);++o<u&&r(n[o],o,n);)t++}else t=null==r||e?1:et(0,r);return p(n,t)}function Rr(n,r,e,t){var o=0,u=n?n.length:o;for(e=e?v.createCallback(e,t,1):ue,r=e(r);o<u;){var a=o+u>>>1;e(n[a])<r?o=a+1:u=a}return o}function Tr(){return sn(an(arguments,!0,!0))}function Dr(n,r,e,t){return"boolean"!=typeof r&&null!=r&&(t=e,e="function"!=typeof r&&t&&t[r]===n?null:r,r=!1),null!=e&&(e=v.createCallback(e,t,3)),
+sn(n,r,e)}function $r(n){return on(n,p(arguments,1))}function Fr(){for(var n=-1,r=arguments.length;++n<r;){var e=arguments[n];if(st(e)||_n(e))var t=t?sn(on(t,e).concat(on(e,t))):e}return t||[]}function Br(){for(var n=arguments.length>1?arguments:arguments[0],r=-1,e=n?lr(At(n,"length")):0,t=we(e<0?0:e);++r<e;)t[r]=At(n,r);return t}function Hr(n,r){var e=-1,t=n?n.length:0,o={};for(r||!t||st(n[0])||(r=[]);++e<t;){var u=n[e];r?o[u]=r[e]:u&&(o[u[0]]=u[1])}return o}function Wr(n,r){if(!$n(r))throw new Ie;
+
+return function(){return--n<1?r.apply(this,arguments):h}}function qr(n,r){return arguments.length>2?gn(n,17,p(arguments,2),null,r):gn(n,1,null,null,r)}function zr(n){for(var r=arguments.length>1?an(arguments,!0,!1,1):On(n),e=-1,t=r.length;++e<t;){var o=r[e];n[o]=gn(n[o],1,null,null,n)}return n}function Kr(n,r){return arguments.length>2?gn(r,19,p(arguments,2),null,n):gn(r,3,null,null,n)}function Ur(){for(var n=arguments,r=n.length;r--;)if(!$n(n[r]))throw new Ie;return function(){for(var r=arguments,e=n.length;e--;)r=[n[e].apply(this,r)];
+
+return r[0]}}function Mr(n,r){return r="number"==typeof r?r:+r||n.length,gn(n,4,null,null,null,r)}function Vr(n,r,e){var t,o,u,a,i,l,c,f=0,s=!1,p=!0;if(!$n(n))throw new Ie;if(r=et(0,r)||0,e===!0){var g=!0;p=!1}else Fn(e)&&(g=e.leading,s="maxWait"in e&&(et(r,e.maxWait)||0),p="trailing"in e?e.trailing:p);var v=function(){var e=r-(Lt()-a);if(e>0)l=Ve(v,e);else{o&&He(o);var s=c;o=l=c=h,s&&(f=Lt(),u=n.apply(i,t),l||o||(t=i=null))}},y=function(){l&&He(l),o=l=c=h,(p||s!==r)&&(f=Lt(),u=n.apply(i,t),l||o||(t=i=null));
+
+};return function(){if(t=arguments,a=Lt(),i=this,c=p&&(l||!g),s===!1)var e=g&&!l;else{o||g||(f=a);var h=s-(a-f),b=h<=0;b?(o&&(o=He(o)),f=a,u=n.apply(i,t)):o||(o=Ve(y,h))}return b&&l?l=He(l):l||r===s||(l=Ve(v,r)),e&&(b=!0,u=n.apply(i,t)),!b||l||o||(t=i=null),u}}function Gr(n){if(!$n(n))throw new Ie;var r=p(arguments,1);return Ve(function(){n.apply(h,r)},1)}function Jr(n,r){if(!$n(n))throw new Ie;var e=p(arguments,2);return Ve(function(){n.apply(h,e)},r)}function Qr(n,r){if(!$n(n))throw new Ie;var e=function(){
+var t=e.cache,o=r?r.apply(this,arguments):m+arguments[0];return Ke.call(t,o)?t[o]:t[o]=n.apply(this,arguments)};return e.cache={},e}function Xr(n){var r,e;if(!$n(n))throw new Ie;return function(){return r?e:(r=!0,e=n.apply(this,arguments),n=null,e)}}function Yr(n){return gn(n,16,p(arguments,1))}function Zr(n){return gn(n,32,null,p(arguments,1))}function ne(n,r,e){var t=!0,o=!0;if(!$n(n))throw new Ie;return e===!1?t=!1:Fn(e)&&(t="leading"in e?e.leading:t,o="trailing"in e?e.trailing:o),J.leading=t,
+J.maxWait=r,J.trailing=o,Vr(n,r,J)}function re(n,r){return gn(r,16,[n])}function ee(n){return function(){return n}}function te(n,r,e){var t=typeof n;if(null==n||"function"==t)return en(n,r,e);if("object"!=t)return ce(n);var o=gt(n),u=o[0],a=n[u];return 1!=o.length||a!==a||Fn(a)?function(r){for(var e=o.length,t=!1;e--&&(t=ln(r[o[e]],n[o[e]],null,!0)););return t}:function(n){var r=n[u];return a===r&&(0!==a||1/a==1/r)}}function oe(n){return null==n?"":Ae(n).replace(_t,vn)}function ue(n){return n}function ae(n,r,e){
+var t=!0,o=r&&On(r);r&&(e||o.length)||(null==e&&(e=r),u=y,r=n,n=v,o=On(r)),e===!1?t=!1:Fn(e)&&"chain"in e&&(t=e.chain);var u=n,a=$n(u);or(o,function(e){var o=n[e]=r[e];a&&(u.prototype[e]=function(){var r=this.__chain__,e=this.__wrapped__,a=[e];Ue.apply(a,arguments);var i=o.apply(n,a);if(t||r){if(e===i&&Fn(i))return this;i=new u(i),i.__chain__=r}return i})})}function ie(){return e._=De,this}function le(){}function ce(n){return function(r){return r[n]}}function fe(n,r,e){var t=null==n,o=null==r;if(null==e&&("boolean"==typeof n&&o?(e=n,
+n=1):o||"boolean"!=typeof r||(e=r,o=!0)),t&&o&&(r=1),n=+n||0,o?(r=n,n=0):r=+r||0,e||n%1||r%1){var u=ut();return tt(n+u*(r-n+parseFloat("1e-"+((u+"").length-1))),r)}return fn(n,r)}function se(n,r){if(n){var e=n[r];return $n(e)?n[r]():e}}function pe(n,r,e){var t=v.templateSettings;n=Ae(n||""),e=jt({},e,t);var o,u=jt({},e.imports,t.imports),i=gt(u),l=Xn(u),c=0,f=e.interpolate||L,s="__p += '",p=Se((e.escape||L).source+"|"+f.source+"|"+(f===A?E:L).source+"|"+(e.evaluate||L).source+"|$","g");n.replace(p,function(r,e,t,u,i,l){
+return t||(t=u),s+=n.slice(c,l).replace(R,a),e&&(s+="' +\n__e("+e+") +\n'"),i&&(o=!0,s+="';\n"+i+";\n__p += '"),t&&(s+="' +\n((__t = ("+t+")) == null ? '' : __t) +\n'"),c=l+r.length,r}),s+="';\n";var g=e.variable,y=g;y||(g="obj",s="with ("+g+") {\n"+s+"\n}\n"),s=(o?s.replace(j,""):s).replace(C,"$1").replace(P,"$1;"),s="function("+g+") {\n"+(y?"":g+" || ("+g+" = {});\n")+"var __t, __p = '', __e = _.escape"+(o?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+s+"return __p\n}";
+
+var b="\n/*\n//# sourceURL="+(e.sourceURL||"/lodash/template/source["+$++ +"]")+"\n*/";try{var d=Ce(i,"return "+s+b).apply(h,l)}catch(m){throw m.source=s,m}return r?d(r):(d.source=s,d)}function ge(n,r,e){n=(n=+n)>-1?n:0;var t=-1,o=we(n);for(r=en(r,e,1);++t<n;)o[t]=r(t);return o}function he(n){return null==n?"":Ae(n).replace(mt,mn)}function ve(n){var r=++b;return Ae(null==n?"":n)+r}function ye(n){return n=new y(n),n.__chain__=!0,n}function be(n,r){return r(n),n}function de(){return this.__chain__=!0,
+this}function me(){return Ae(this.__wrapped__)}function _e(){return this.__wrapped__}e=e?un.defaults(nn.Object(),e,un.pick(nn,T)):nn;var we=e.Array,xe=e.Boolean,je=e.Date,ke=e.Error,Ce=e.Function,Pe=e.Math,Ee=e.Number,Oe=e.Object,Se=e.RegExp,Ae=e.String,Ie=e.TypeError,Le=[],Ne=ke.prototype,Re=Oe.prototype,Te=Ae.prototype,De=e._,$e=Re.toString,Fe=Se("^"+Ae($e).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),Be=Pe.ceil,He=e.clearTimeout,We=Pe.floor,qe=Ce.prototype.toString,ze=bn(ze=Oe.getPrototypeOf)&&ze,Ke=Re.hasOwnProperty,Ue=Le.push,Me=Re.propertyIsEnumerable,Ve=e.setTimeout,Ge=Le.splice,Je=Le.unshift,Qe=function(){
+try{var n={},r=bn(r=Oe.defineProperty)&&r,e=r(n,n,n)&&r}catch(t){}return e}(),Xe=bn(Xe=Oe.create)&&Xe,Ye=bn(Ye=we.isArray)&&Ye,Ze=e.isFinite,nt=e.isNaN,rt=bn(rt=Oe.keys)&&rt,et=Pe.max,tt=Pe.min,ot=e.parseInt,ut=Pe.random,at={};at[B]=we,at[H]=xe,at[W]=je,at[z]=Ce,at[U]=Oe,at[K]=Ee,at[M]=Se,at[V]=Ae;var it={};it[B]=it[W]=it[K]={constructor:!0,toLocaleString:!0,toString:!0,valueOf:!0},it[H]=it[V]={constructor:!0,toString:!0,valueOf:!0},it[q]=it[z]=it[M]={constructor:!0,toString:!0},it[U]={constructor:!0
+},function(){for(var n=D.length;n--;){var r=D[n];for(var e in it)Ke.call(it,e)&&!Ke.call(it[e],r)&&(it[e][r]=!1)}}(),y.prototype=v.prototype;var lt=v.support={};!function(){var n=function(){this.x=1},r={0:1,length:1},t=[];n.prototype={valueOf:1,y:1};for(var o in new n)t.push(o);for(o in arguments);lt.argsClass=$e.call(arguments)==F,lt.argsObject=arguments.constructor==Oe&&!(arguments instanceof we),lt.enumErrorProps=Me.call(Ne,"message")||Me.call(Ne,"name"),lt.enumPrototypes=Me.call(n,"prototype"),
+lt.funcDecomp=!bn(e.WinRTError)&&N.test(g),lt.funcNames="string"==typeof Ce.name,lt.nonEnumArgs=0!=o,lt.nonEnumShadows=!/valueOf/.test(t),lt.ownLast="x"!=t[0],lt.spliceObjects=(Le.splice.call(r,0,1),!r[0]),lt.unindexedChars="x"[0]+Oe("x")[0]!="xx";try{lt.nodeClass=!($e.call(document)==U&&!({toString:0}+""))}catch(u){lt.nodeClass=!0}}(1),v.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:A,variable:"",imports:{_:v}};var ct=function(n){var r="var index, iterable = "+n.firstArg+", result = "+n.init+";\nif (!iterable) return result;\n"+n.top+";";
+
+n.array?(r+="\nvar length = iterable.length; index = -1;\nif ("+n.array+") { ",lt.unindexedChars&&(r+="\n if (isString(iterable)) {\n iterable = iterable.split('')\n } "),r+="\n while (++index < length) {\n "+n.loop+";\n }\n}\nelse { "):lt.nonEnumArgs&&(r+="\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += '';\n "+n.loop+";\n }\n } else { "),lt.enumPrototypes&&(r+="\n var skipProto = typeof iterable == 'function';\n "),
+lt.enumErrorProps&&(r+="\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ");var e=[];if(lt.enumPrototypes&&e.push('!(skipProto && index == "prototype")'),lt.enumErrorProps&&e.push('!(skipErrorProps && (index == "message" || index == "name"))'),n.useHas&&n.keys)r+="\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n",
+e.length&&(r+=" if ("+e.join(" && ")+") {\n "),r+=n.loop+"; ",e.length&&(r+="\n }"),r+="\n } ";else if(r+="\n for (index in iterable) {\n",n.useHas&&e.push("hasOwnProperty.call(iterable, index)"),e.length&&(r+=" if ("+e.join(" && ")+") {\n "),r+=n.loop+"; ",e.length&&(r+="\n }"),r+="\n } ",lt.nonEnumShadows){for(r+="\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ",
+k=0;k<7;k++)r+="\n index = '"+n.shadowedProps[k]+"';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))",n.useHas||(r+=" || (!nonEnum[index] && iterable[index] !== objectProto[index])"),r+=") {\n "+n.loop+";\n } ";r+="\n } "}return(n.array||lt.nonEnumArgs)&&(r+="\n}"),r+=n.bottom+";\nreturn result"};Xe||(rn=function(){function n(){}return function(r){if(Fn(r)){n.prototype=r;var t=new n;n.prototype=null}return t||e.Object()}}());var ft=Qe?function(n,r){
+Q.value=r,Qe(n,"__bindData__",Q),Q.value=null}:le;lt.argsClass||(_n=function(n){return n&&"object"==typeof n&&"number"==typeof n.length&&Ke.call(n,"callee")&&!Me.call(n,"callee")||!1});var st=Ye||function(n){return n&&"object"==typeof n&&"number"==typeof n.length&&$e.call(n)==B||!1},pt=hn({args:"object",init:"[]",top:"if (!(objectTypes[typeof object])) return result",loop:"result.push(index)"}),gt=rt?function(n){return Fn(n)?lt.enumPrototypes&&"function"==typeof n||lt.nonEnumArgs&&n.length&&_n(n)?pt(n):rt(n):[];
+
+}:pt,ht={args:"collection, callback, thisArg",top:"callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",array:"typeof length == 'number'",keys:gt,loop:"if (callback(iterable[index], index, collection) === false) return result"},vt={args:"object, source, guard",top:"var args = arguments,\n argsIndex = 0,\n argsLength = typeof guard == 'number' ? 2 : args.length;\nwhile (++argsIndex < argsLength) {\n iterable = args[argsIndex];\n if (iterable && objectTypes[typeof iterable]) {",
+keys:gt,loop:"if (typeof result[index] == 'undefined') result[index] = iterable[index]",bottom:" }\n}"},yt={top:"if (!objectTypes[typeof iterable]) return result;\n"+ht.top,array:!1},bt={"&":"&","<":"<",">":">",'"':""","'":"'"},dt=An(bt),mt=Se("("+gt(dt).join("|")+")","g"),_t=Se("["+gt(bt).join("")+"]","g"),wt=hn(ht),xt=hn(vt,{top:vt.top.replace(";",";\nif (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n callback = args[--argsLength];\n}"),
+loop:"result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]"}),jt=hn(vt),kt=hn(ht,yt,{useHas:!1}),Ct=hn(ht,yt);$n(/x/)&&($n=function(n){return"function"==typeof n&&$e.call(n)==z});var Pt=ze?function(n){if(!n||$e.call(n)!=U||!lt.argsClass&&_n(n))return!1;var r=n.valueOf,e=bn(r)&&(e=ze(r))&&ze(e);return e?n==e||ze(n)==e:dn(n)}:dn,Et=pn(function(n,r,e){Ke.call(n,e)?n[e]++:n[e]=1}),Ot=pn(function(n,r,e){(Ke.call(n,e)?n[e]:n[e]=[]).push(r)}),St=pn(function(n,r,e){n[e]=r;
+
+}),At=ir,It=rr,Lt=bn(Lt=je.now)&&Lt||function(){return(new je).getTime()},Nt=8==ot(x+"08")?ot:function(n,r){return ot(zn(n)?n.replace(I,""):n,r||0)};return v.after=Wr,v.assign=xt,v.at=Yn,v.bind=qr,v.bindAll=zr,v.bindKey=Kr,v.chain=ye,v.compact=mr,v.compose=Ur,v.constant=ee,v.countBy=Et,v.create=jn,v.createCallback=te,v.curry=Mr,v.debounce=Vr,v.defaults=jt,v.defer=Gr,v.delay=Jr,v.difference=_r,v.filter=rr,v.flatten=kr,v.forEach=or,v.forEachRight=ur,v.forIn=kt,v.forInRight=Pn,v.forOwn=Ct,v.forOwnRight=En,
+v.functions=On,v.groupBy=Ot,v.indexBy=St,v.initial=Pr,v.intersection=Er,v.invert=An,v.invoke=ar,v.keys=gt,v.map=ir,v.mapValues=Un,v.max=lr,v.memoize=Qr,v.merge=Mn,v.min=cr,v.omit=Vn,v.once=Xr,v.pairs=Gn,v.partial=Yr,v.partialRight=Zr,v.pick=Jn,v.pluck=At,v.property=ce,v.pull=Ar,v.range=Ir,v.reject=pr,v.remove=Lr,v.rest=Nr,v.shuffle=hr,v.sortBy=br,v.tap=be,v.throttle=ne,v.times=ge,v.toArray=dr,v.transform=Qn,v.union=Tr,v.uniq=Dr,v.values=Xn,v.where=It,v.without=$r,v.wrap=re,v.xor=Fr,v.zip=Br,v.zipObject=Hr,
+v.collect=ir,v.drop=Nr,v.each=or,v.eachRight=ur,v.extend=xt,v.methods=On,v.object=Hr,v.select=rr,v.tail=Nr,v.unique=Dr,v.unzip=Br,ae(v),v.clone=wn,v.cloneDeep=xn,v.contains=Zn,v.escape=oe,v.every=nr,v.find=er,v.findIndex=wr,v.findKey=kn,v.findLast=tr,v.findLastIndex=xr,v.findLastKey=Cn,v.has=Sn,v.identity=ue,v.indexOf=Cr,v.isArguments=_n,v.isArray=st,v.isBoolean=In,v.isDate=Ln,v.isElement=Nn,v.isEmpty=Rn,v.isEqual=Tn,v.isFinite=Dn,v.isFunction=$n,v.isNaN=Bn,v.isNull=Hn,v.isNumber=Wn,v.isObject=Fn,
+v.isPlainObject=Pt,v.isRegExp=qn,v.isString=zn,v.isUndefined=Kn,v.lastIndexOf=Sr,v.mixin=ae,v.noConflict=ie,v.noop=le,v.now=Lt,v.parseInt=Nt,v.random=fe,v.reduce=fr,v.reduceRight=sr,v.result=se,v.runInContext=g,v.size=vr,v.some=yr,v.sortedIndex=Rr,v.template=pe,v.unescape=he,v.uniqueId=ve,v.all=nr,v.any=yr,v.detect=er,v.findWhere=er,v.foldl=fr,v.foldr=sr,v.include=Zn,v.inject=fr,ae(function(){var n={};return Ct(v,function(r,e){v.prototype[e]||(n[e]=r)}),n}(),!1),v.first=jr,v.last=Or,v.sample=gr,v.take=jr,
+v.head=jr,Ct(v,function(n,r){var e="sample"!==r;v.prototype[r]||(v.prototype[r]=function(r,t){var o=this.__chain__,u=n(this.__wrapped__,r,t);return o||null!=r&&(!t||e&&"function"==typeof r)?new y(u,o):u})}),v.VERSION="2.4.2",v.prototype.chain=de,v.prototype.toString=me,v.prototype.value=_e,v.prototype.valueOf=_e,wt(["join","pop","shift"],function(n){var r=Le[n];v.prototype[n]=function(){var n=this.__chain__,e=r.apply(this.__wrapped__,arguments);return n?new y(e,n):e}}),wt(["push","reverse","sort","unshift"],function(n){
+var r=Le[n];v.prototype[n]=function(){return r.apply(this.__wrapped__,arguments),this}}),wt(["concat","slice","splice"],function(n){var r=Le[n];v.prototype[n]=function(){return new y(r.apply(this.__wrapped__,arguments),this.__chain__)}}),lt.spliceObjects||wt(["pop","shift","splice"],function(n){var r=Le[n],e="splice"==n;v.prototype[n]=function(){var n=this.__chain__,t=this.__wrapped__,o=r.apply(t,arguments);return 0===t.length&&delete t[0],n||e?new y(o,n):o}}),v}var h,v=[],y=[],b=0,d={},m=+new Date+"",_=75,w=40,x=" \v\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",j=/\b__p \+= '';/g,C=/\b(__p \+=) '' \+/g,P=/(__e\(.*?\)|\b__t\)) \+\n'';/g,E=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,O=/\w*$/,S=/^\s*function[ \n\r\t]+\w/,A=/<%=([\s\S]+?)%>/g,I=RegExp("^["+x+"]*0+(?=.$)"),L=/($^)/,N=/\bthis\b/,R=/['\n\r\t\u2028\u2029\\]/g,T=["Array","Boolean","Date","Error","Function","Math","Number","Object","RegExp","String","_","attachEvent","clearTimeout","isFinite","isNaN","parseInt","setTimeout"],D=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],$=0,F="[object Arguments]",B="[object Array]",H="[object Boolean]",W="[object Date]",q="[object Error]",z="[object Function]",K="[object Number]",U="[object Object]",M="[object RegExp]",V="[object String]",G={};
+
+G[z]=!1,G[F]=G[B]=G[H]=G[W]=G[K]=G[U]=G[M]=G[V]=!0;var J={leading:!1,maxWait:0,trailing:!1},Q={configurable:!1,enumerable:!1,value:null,writable:!1},X={args:"",array:null,bottom:"",firstArg:"",init:"",keys:null,loop:"",shadowedProps:null,support:null,top:"",useHas:!1},Y={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,undefined:!1},Z={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"},nn=Y[typeof window]&&window||this,rn=Y[typeof exports]&&exports&&!exports.nodeType&&exports,en=Y[typeof module]&&module&&!module.nodeType&&module,tn=en&&en.exports===rn&&rn,on=Y[typeof global]&&global;
+
+!on||on.global!==on&&on.window!==on||(nn=on);var un=g();"function"==typeof define&&"object"==typeof define.amd&&define.amd?(nn._=un,define(function(){return un})):rn&&en?tn?(en.exports=un)._=un:rn._=un:nn._=un}).call(this);
\ No newline at end of file
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 (Custom Build) <https://lodash.com/>
+ * Build: `lodash modern -o ./dist/lodash.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <https://lodash.com/license>
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre ES5 environments */
+ var undefined;
+
+ /** Used to pool arrays and objects used internally */
+ var arrayPool = [],
+ objectPool = [];
+
+ /** Used to generate unique IDs */
+ var idCounter = 0;
+
+ /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+ var keyPrefix = +new Date + '';
+
+ /** Used as the size when optimizations are enabled for large arrays */
+ var largeArraySize = 75;
+
+ /** Used as the max size of the `arrayPool` and `objectPool` */
+ var maxPoolSize = 40;
+
+ /** Used to detect and test whitespace */
+ var whitespace = (
+ // whitespace
+ ' \t\x0B\f\xA0\ufeff' +
+
+ // line terminators
+ '\n\r\u2028\u2029' +
+
+ // unicode category "Zs" space separators
+ '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+ );
+
+ /** Used to match empty string literals in compiled template source */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+ /**
+ * Used to match ES6 template delimiters
+ * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+ /** Used to match regexp flags from their coerced string values */
+ var reFlags = /\w*$/;
+
+ /** Used to detected named functions */
+ var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+ /** Used to match "interpolate" template delimiters */
+ var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+ /** Used to match leading whitespace and zeros to be removed */
+ var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+ /** Used to ensure capturing order of template delimiters */
+ var reNoMatch = /($^)/;
+
+ /** Used to detect functions containing a `this` reference */
+ var reThis = /\bthis\b/;
+
+ /** Used to match unescaped characters in compiled string literals */
+ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+ /** Used to assign default `context` object properties */
+ var contextProps = [
+ 'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
+ 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+ 'parseInt', 'setTimeout'
+ ];
+
+ /** Used to make template sourceURLs easier to identify */
+ var templateCounter = 0;
+
+ /** `Object#toString` result shortcuts */
+ var argsClass = '[object Arguments]',
+ arrayClass = '[object Array]',
+ boolClass = '[object Boolean]',
+ dateClass = '[object Date]',
+ funcClass = '[object Function]',
+ numberClass = '[object Number]',
+ objectClass = '[object Object]',
+ regexpClass = '[object RegExp]',
+ stringClass = '[object String]';
+
+ /** Used to identify object classifications that `_.clone` supports */
+ var cloneableClasses = {};
+ cloneableClasses[funcClass] = false;
+ cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+ cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+ cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+ cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+ /** Used as an internal `_.debounce` options object */
+ var debounceOptions = {
+ 'leading': false,
+ 'maxWait': 0,
+ 'trailing': false
+ };
+
+ /** Used as the property descriptor for `__bindData__` */
+ var descriptor = {
+ 'configurable': false,
+ 'enumerable': false,
+ 'value': null,
+ 'writable': false
+ };
+
+ /** Used to determine if values are of the language type Object */
+ var objectTypes = {
+ 'boolean': false,
+ 'function': true,
+ 'object': true,
+ 'number': false,
+ 'string': false,
+ 'undefined': false
+ };
+
+ /** Used to escape characters for inclusion in compiled string literals */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ /** Used as a reference to the global object */
+ var root = (objectTypes[typeof window] && window) || this;
+
+ /** Detect free variable `exports` */
+ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+ /** Detect free variable `module` */
+ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+ /** Detect the popular CommonJS extension `module.exports` */
+ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+ /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+ var freeGlobal = objectTypes[typeof global] && global;
+ if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+ root = freeGlobal;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.indexOf` without support for binary searches
+ * or `fromIndex` constraints.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * An implementation of `_.contains` for cache objects that mimics the return
+ * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+ *
+ * @private
+ * @param {Object} cache The cache object to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `0` if `value` is found, else `-1`.
+ */
+ function cacheIndexOf(cache, value) {
+ var type = typeof value;
+ cache = cache.cache;
+
+ if (type == 'boolean' || value == null) {
+ return cache[value] ? 0 : -1;
+ }
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value;
+ cache = (cache = cache[type]) && cache[key];
+
+ return type == 'object'
+ ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+ : (cache ? 0 : -1);
+ }
+
+ /**
+ * Adds a given value to the corresponding cache object.
+ *
+ * @private
+ * @param {*} value The value to add to the cache.
+ */
+ function cachePush(value) {
+ var cache = this.cache,
+ type = typeof value;
+
+ if (type == 'boolean' || value == null) {
+ cache[value] = true;
+ } else {
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value,
+ typeCache = cache[type] || (cache[type] = {});
+
+ if (type == 'object') {
+ (typeCache[key] || (typeCache[key] = [])).push(value);
+ } else {
+ typeCache[key] = true;
+ }
+ }
+ }
+
+ /**
+ * Used by `_.max` and `_.min` as the default callback when a given
+ * collection is a string value.
+ *
+ * @private
+ * @param {string} value The character to inspect.
+ * @returns {number} Returns the code unit of given character.
+ */
+ function charAtCallback(value) {
+ return value.charCodeAt(0);
+ }
+
+ /**
+ * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+ * them in ascending order.
+ *
+ * @private
+ * @param {Object} a The object to compare to `b`.
+ * @param {Object} b The object to compare to `a`.
+ * @returns {number} Returns the sort order indicator of `1` or `-1`.
+ */
+ function compareAscending(a, b) {
+ var ac = a.criteria,
+ bc = b.criteria,
+ index = -1,
+ length = ac.length;
+
+ while (++index < length) {
+ var value = ac[index],
+ other = bc[index];
+
+ if (value !== other) {
+ if (value > other || typeof value == 'undefined') {
+ return 1;
+ }
+ if (value < other || typeof other == 'undefined') {
+ return -1;
+ }
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to return the same value for
+ // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See http://code.google.com/p/v8/issues/detail?id=90
+ return a.index - b.index;
+ }
+
+ /**
+ * Creates a cache object to optimize linear searches of large arrays.
+ *
+ * @private
+ * @param {Array} [array=[]] The array to search.
+ * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+ */
+ function createCache(array) {
+ var index = -1,
+ length = array.length,
+ first = array[0],
+ mid = array[(length / 2) | 0],
+ last = array[length - 1];
+
+ if (first && typeof first == 'object' &&
+ mid && typeof mid == 'object' && last && typeof last == 'object') {
+ return false;
+ }
+ var cache = getObject();
+ cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+ var result = getObject();
+ result.array = array;
+ result.cache = cache;
+ result.push = cachePush;
+
+ while (++index < length) {
+ result.push(array[index]);
+ }
+ return result;
+ }
+
+ /**
+ * Used by `template` to escape characters for inclusion in compiled
+ * string literals.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(match) {
+ return '\\' + stringEscapes[match];
+ }
+
+ /**
+ * Gets an array from the array pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Array} The array from the pool.
+ */
+ function getArray() {
+ return arrayPool.pop() || [];
+ }
+
+ /**
+ * Gets an object from the object pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Object} The object from the pool.
+ */
+ function getObject() {
+ return objectPool.pop() || {
+ 'array': null,
+ 'cache': null,
+ 'criteria': null,
+ 'false': false,
+ 'index': 0,
+ 'null': false,
+ 'number': null,
+ 'object': null,
+ 'push': null,
+ 'string': null,
+ 'true': false,
+ 'undefined': false,
+ 'value': null
+ };
+ }
+
+ /**
+ * Releases the given array back to the array pool.
+ *
+ * @private
+ * @param {Array} [array] The array to release.
+ */
+ function releaseArray(array) {
+ array.length = 0;
+ if (arrayPool.length < maxPoolSize) {
+ arrayPool.push(array);
+ }
+ }
+
+ /**
+ * Releases the given object back to the object pool.
+ *
+ * @private
+ * @param {Object} [object] The object to release.
+ */
+ function releaseObject(object) {
+ var cache = object.cache;
+ if (cache) {
+ releaseObject(cache);
+ }
+ object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+ if (objectPool.length < maxPoolSize) {
+ objectPool.push(object);
+ }
+ }
+
+ /**
+ * Slices the `collection` from the `start` index up to, but not including,
+ * the `end` index.
+ *
+ * Note: This function is used instead of `Array#slice` to support node lists
+ * in IE < 9 and to ensure dense arrays are returned.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to slice.
+ * @param {number} start The start index.
+ * @param {number} end The end index.
+ * @returns {Array} Returns the new array.
+ */
+ function slice(array, start, end) {
+ start || (start = 0);
+ if (typeof end == 'undefined') {
+ end = array ? array.length : 0;
+ }
+ var index = -1,
+ length = end - start || 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = array[start + index];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Create a new `lodash` function using the given context object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} [context=root] The context object.
+ * @returns {Function} Returns the `lodash` function.
+ */
+ function runInContext(context) {
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See http://es5.github.io/#x11.1.5.
+ context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+ /** Native constructor references */
+ var Array = context.Array,
+ Boolean = context.Boolean,
+ Date = context.Date,
+ Function = context.Function,
+ Math = context.Math,
+ Number = context.Number,
+ Object = context.Object,
+ RegExp = context.RegExp,
+ String = context.String,
+ TypeError = context.TypeError;
+
+ /**
+ * Used for `Array` method references.
+ *
+ * Normally `Array.prototype` would suffice, however, using an array literal
+ * avoids issues in Narwhal.
+ */
+ var arrayRef = [];
+
+ /** Used for native method references */
+ var objectProto = Object.prototype;
+
+ /** Used to restore the original `_` reference in `noConflict` */
+ var oldDash = context._;
+
+ /** Used to resolve the internal [[Class]] of values */
+ var toString = objectProto.toString;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(toString)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/toString| for [^\]]+/g, '.*?') + '$'
+ );
+
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ clearTimeout = context.clearTimeout,
+ floor = Math.floor,
+ fnToString = Function.prototype.toString,
+ getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+ hasOwnProperty = objectProto.hasOwnProperty,
+ push = arrayRef.push,
+ setTimeout = context.setTimeout,
+ splice = arrayRef.splice,
+ unshift = arrayRef.unshift;
+
+ /** Used to set meta data on functions */
+ var defineProperty = (function() {
+ // IE 8 only accepts DOM elements
+ try {
+ var o = {},
+ func = isNative(func = Object.defineProperty) && func,
+ result = func(o, o, o) && func;
+ } catch(e) { }
+ return result;
+ }());
+
+ /* Native method shortcuts for methods with the same name as other `lodash` methods */
+ var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+ nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+ nativeIsFinite = context.isFinite,
+ nativeIsNaN = context.isNaN,
+ nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeParseInt = context.parseInt,
+ nativeRandom = Math.random;
+
+ /** Used to lookup a built-in constructor by [[Class]] */
+ var ctorByClass = {};
+ ctorByClass[arrayClass] = Array;
+ ctorByClass[boolClass] = Boolean;
+ ctorByClass[dateClass] = Date;
+ ctorByClass[funcClass] = Function;
+ ctorByClass[objectClass] = Object;
+ ctorByClass[numberClass] = Number;
+ ctorByClass[regexpClass] = RegExp;
+ ctorByClass[stringClass] = String;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps the given value to enable intuitive
+ * method chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+ * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+ * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+ * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+ * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+ * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * and `zip`
+ *
+ * The non-chainable wrapper functions are:
+ * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+ * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+ * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+ * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+ * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+ * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+ * `template`, `unescape`, `uniqueId`, and `value`
+ *
+ * The wrapper functions `first` and `last` return wrapped values when `n` is
+ * provided, otherwise they return unwrapped values.
+ *
+ * Explicit chaining can be enabled by using the `_.chain` method.
+ *
+ * @name _
+ * @constructor
+ * @category Chaining
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(num) {
+ * return num * num;
+ * });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+ return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+ ? value
+ : new lodashWrapper(value);
+ }
+
+ /**
+ * A fast path for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @param {boolean} chainAll A flag to enable chaining for all methods
+ * @returns {Object} Returns a `lodash` instance.
+ */
+ function lodashWrapper(value, chainAll) {
+ this.__chain__ = !!chainAll;
+ this.__wrapped__ = value;
+ }
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
+
+ /**
+ * An object used to flag environments features.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ var support = lodash.support = {};
+
+ /**
+ * Detect if functions can be decompiled by `Function#toString`
+ * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+ /**
+ * Detect if `Function#name` is supported (all but IE).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcNames = typeof Function.name == 'string';
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in
+ * embedded Ruby (ERB). Change the following template settings to use alternative
+ * delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': /<%-([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': /<%([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
+
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type string
+ */
+ 'variable': '',
+
+ /**
+ * Used to import variables into the compiled template.
+ *
+ * @memberOf _.templateSettings
+ * @type Object
+ */
+ 'imports': {
+
+ /**
+ * A reference to the `lodash` function.
+ *
+ * @memberOf _.templateSettings.imports
+ * @type Function
+ */
+ '_': lodash
+ }
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.bind` that creates the bound function and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new bound function.
+ */
+ function baseBind(bindData) {
+ var func = bindData[0],
+ partialArgs = bindData[2],
+ thisArg = bindData[4];
+
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.io/#x15.3.4.5
+ if (partialArgs) {
+ // avoid `arguments` object deoptimizations by using `slice` instead
+ // of `Array.prototype.slice.call` and not assigning `arguments` to a
+ // variable as a ternary expression
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ // mimic the constructor's `return` behavior
+ // http://es5.github.io/#x13.2.2
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ var thisBinding = baseCreate(func.prototype),
+ result = func.apply(thisBinding, args || arguments);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisArg, args || arguments);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.clone` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+ function baseClone(value, isDeep, callback, stackA, stackB) {
+ if (callback) {
+ var result = callback(value);
+ if (typeof result != 'undefined') {
+ return result;
+ }
+ }
+ // inspect [[Class]]
+ var isObj = isObject(value);
+ if (isObj) {
+ var className = toString.call(value);
+ if (!cloneableClasses[className]) {
+ return value;
+ }
+ var ctor = ctorByClass[className];
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return new ctor(+value);
+
+ case numberClass:
+ case stringClass:
+ return new ctor(value);
+
+ case regexpClass:
+ result = ctor(value.source, reFlags.exec(value));
+ result.lastIndex = value.lastIndex;
+ return result;
+ }
+ } else {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isDeep) {
+ // check for circular references and return corresponding clone
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == value) {
+ return stackB[length];
+ }
+ }
+ result = isArr ? ctor(value.length) : {};
+ }
+ else {
+ result = isArr ? slice(value) : assign({}, value);
+ }
+ // add array properties assigned by `RegExp#exec`
+ if (isArr) {
+ if (hasOwnProperty.call(value, 'index')) {
+ result.index = value.index;
+ }
+ if (hasOwnProperty.call(value, 'input')) {
+ result.input = value.input;
+ }
+ }
+ // exit for shallow clone
+ if (!isDeep) {
+ return result;
+ }
+ // add the source value to the stack of traversed objects
+ // and associate it with its clone
+ stackA.push(value);
+ stackB.push(result);
+
+ // recursively populate clone (susceptible to call stack limits)
+ (isArr ? forEach : forOwn)(value, function(objValue, key) {
+ result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+ });
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ function baseCreate(prototype, properties) {
+ return isObject(prototype) ? nativeCreate(prototype) : {};
+ }
+ // fallback for browsers without `Object.create`
+ if (!nativeCreate) {
+ baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || context.Object();
+ };
+ }());
+ }
+
+ /**
+ * The base implementation of `_.createCallback` without support for creating
+ * "_.pluck" or "_.where" style callbacks.
+ *
+ * @private
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ */
+ function baseCreateCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ // exit early for no `thisArg` or already bound by `Function#bind`
+ if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+ return func;
+ }
+ var bindData = func.__bindData__;
+ if (typeof bindData == 'undefined') {
+ if (support.funcNames) {
+ bindData = !func.name;
+ }
+ bindData = bindData || !support.funcDecomp;
+ if (!bindData) {
+ var source = fnToString.call(func);
+ if (!support.funcNames) {
+ bindData = !reFuncName.test(source);
+ }
+ if (!bindData) {
+ // checks if `func` references the `this` keyword and stores the result
+ bindData = reThis.test(source);
+ setBindData(func, bindData);
+ }
+ }
+ }
+ // exit early if there are no `this` references or `func` is bound
+ if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 2: return function(a, b) {
+ return func.call(thisArg, a, b);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ }
+ return bind(func, thisArg);
+ }
+
+ /**
+ * The base implementation of `createWrapper` that creates the wrapper and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new function.
+ */
+ function baseCreateWrapper(bindData) {
+ var func = bindData[0],
+ bitmask = bindData[1],
+ partialArgs = bindData[2],
+ partialRightArgs = bindData[3],
+ thisArg = bindData[4],
+ arity = bindData[5];
+
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ key = func;
+
+ function bound() {
+ var thisBinding = isBind ? thisArg : this;
+ if (partialArgs) {
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ if (partialRightArgs || isCurry) {
+ args || (args = slice(arguments));
+ if (partialRightArgs) {
+ push.apply(args, partialRightArgs);
+ }
+ if (isCurry && args.length < arity) {
+ bitmask |= 16 & ~32;
+ return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+ }
+ }
+ args || (args = arguments);
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (this instanceof bound) {
+ thisBinding = baseCreate(func.prototype);
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.difference` that accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {Array} [values] The array of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+ result = [];
+
+ if (isLarge) {
+ var cache = createCache(values);
+ if (cache) {
+ indexOf = cacheIndexOf;
+ values = cache;
+ } else {
+ isLarge = false;
+ }
+ }
+ while (++index < length) {
+ var value = array[index];
+ if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseObject(values);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` without support for callback
+ * shorthands or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+ * @param {number} [fromIndex=0] The index to start from.
+ * @returns {Array} Returns a new flattened array.
+ */
+ function baseFlatten(array, isShallow, isStrict, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+
+ if (value && typeof value == 'object' && typeof value.length == 'number'
+ && (isArray(value) || isArguments(value))) {
+ // recursively flatten arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ value = baseFlatten(value, isShallow, isStrict);
+ }
+ var valIndex = -1,
+ valLength = value.length,
+ resIndex = result.length;
+
+ result.length += valLength;
+ while (++valIndex < valLength) {
+ result[resIndex++] = value[valIndex];
+ }
+ } else if (!isStrict) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+ * that allows partial "_.where" style comparisons.
+ *
+ * @private
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+ // used to indicate that when comparing objects, `a` has at least the properties of `b`
+ if (callback) {
+ var result = callback(a, b);
+ if (typeof result != 'undefined') {
+ return !!result;
+ }
+ }
+ // exit early for identical values
+ if (a === b) {
+ // treat `+0` vs. `-0` as not equal
+ return a !== 0 || (1 / a == 1 / b);
+ }
+ var type = typeof a,
+ otherType = typeof b;
+
+ // exit early for unlike primitive values
+ if (a === a &&
+ !(a && objectTypes[type]) &&
+ !(b && objectTypes[otherType])) {
+ return false;
+ }
+ // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+ // http://es5.github.io/#x15.3.4.4
+ if (a == null || b == null) {
+ return a === b;
+ }
+ // compare [[Class]] names
+ var className = toString.call(a),
+ otherClass = toString.call(b);
+
+ if (className == argsClass) {
+ className = objectClass;
+ }
+ if (otherClass == argsClass) {
+ otherClass = objectClass;
+ }
+ if (className != otherClass) {
+ return false;
+ }
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ // coerce dates and booleans to numbers, dates to milliseconds and booleans
+ // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+ return +a == +b;
+
+ case numberClass:
+ // treat `NaN` vs. `NaN` as equal
+ return (a != +a)
+ ? b != +b
+ // but treat `+0` vs. `-0` as not equal
+ : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+ case regexpClass:
+ case stringClass:
+ // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+ // treat string primitives and their corresponding object instances as equal
+ return a == String(b);
+ }
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ // unwrap any `lodash` wrapped values
+ var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+ bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+ if (aWrapped || bWrapped) {
+ return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+ }
+ // exit for functions and DOM nodes
+ if (className != objectClass) {
+ return false;
+ }
+ // in older versions of Opera, `arguments` objects have `Array` constructors
+ var ctorA = a.constructor,
+ ctorB = b.constructor;
+
+ // non `Object` object instances with different constructors are not equal
+ if (ctorA != ctorB &&
+ !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+ ('constructor' in a && 'constructor' in b)
+ ) {
+ return false;
+ }
+ }
+ // assume cyclic structures are equal
+ // the algorithm for detecting cyclic structures is adapted from ES 5.1
+ // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
+ }
+ }
+ var size = 0;
+ result = true;
+
+ // add `a` and `b` to the stack of traversed objects
+ stackA.push(a);
+ stackB.push(b);
+
+ // recursively compare objects and arrays (susceptible to call stack limits)
+ if (isArr) {
+ // compare lengths to determine if a deep comparison is necessary
+ length = a.length;
+ size = b.length;
+ result = size == length;
+
+ if (result || isWhere) {
+ // deep compare the contents, ignoring non-numeric properties
+ while (size--) {
+ var index = length,
+ value = b[size];
+
+ if (isWhere) {
+ while (index--) {
+ if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+ // which, in this case, is more costly
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ // count the number of properties.
+ size++;
+ // deep compare each property value.
+ return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+ }
+ });
+
+ if (result && !isWhere) {
+ // ensure both objects have the same number of properties
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ // `size` will be `-1` if `a` has more properties than `b`
+ return (result = --size > -1);
+ }
+ });
+ }
+ }
+ stackA.pop();
+ stackB.pop();
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.merge` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates values with source counterparts.
+ */
+ function baseMerge(object, source, callback, stackA, stackB) {
+ (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+ var found,
+ isArr,
+ result = source,
+ value = object[key];
+
+ if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+ // avoid merging previously merged cyclic sources
+ var stackLength = stackA.length;
+ while (stackLength--) {
+ if ((found = stackA[stackLength] == source)) {
+ value = stackB[stackLength];
+ break;
+ }
+ }
+ if (!found) {
+ var isShallow;
+ if (callback) {
+ result = callback(value, source);
+ if ((isShallow = typeof result != 'undefined')) {
+ value = result;
+ }
+ }
+ if (!isShallow) {
+ value = isArr
+ ? (isArray(value) ? value : [])
+ : (isPlainObject(value) ? value : {});
+ }
+ // add `source` and associated `value` to the stack of traversed objects
+ stackA.push(source);
+ stackB.push(value);
+
+ // recursively merge objects and arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ baseMerge(value, source, callback, stackA, stackB);
+ }
+ }
+ }
+ else {
+ if (callback) {
+ result = callback(value, source);
+ if (typeof result == 'undefined') {
+ result = source;
+ }
+ }
+ if (typeof result != 'undefined') {
+ value = result;
+ }
+ }
+ object[key] = value;
+ });
+ }
+
+ /**
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * The base implementation of `_.uniq` without support for callback shorthands
+ * or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function} [callback] The function called per iteration.
+ * @returns {Array} Returns a duplicate-value-free array.
+ */
+ function baseUniq(array, isSorted, callback) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [];
+
+ var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+ seen = (callback || isLarge) ? getArray() : result;
+
+ if (isLarge) {
+ var cache = createCache(seen);
+ indexOf = cacheIndexOf;
+ seen = cache;
+ }
+ while (++index < length) {
+ var value = array[index],
+ computed = callback ? callback(value, index, array) : value;
+
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : indexOf(seen, computed) < 0
+ ) {
+ if (callback || isLarge) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseArray(seen.array);
+ releaseObject(seen);
+ } else if (callback) {
+ releaseArray(seen);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a function that aggregates a collection, creating an object composed
+ * of keys generated from the results of running each element of the collection
+ * through a callback. The given `setter` function sets the keys and values
+ * of the composed object.
+ *
+ * @private
+ * @param {Function} setter The setter function.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter) {
+ return function(collection, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ setter(result, value, callback(value, index, collection), collection);
+ }
+ } else {
+ forOwn(collection, function(value, key, collection) {
+ setter(result, value, callback(value, key, collection), collection);
+ });
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, either curries or invokes `func`
+ * with an optional `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of method flags to compose.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry`
+ * 8 - `_.curry` (bound)
+ * 16 - `_.partial`
+ * 32 - `_.partialRight`
+ * @param {Array} [partialArgs] An array of arguments to prepend to those
+ * provided to the new function.
+ * @param {Array} [partialRightArgs] An array of arguments to append to those
+ * provided to the new function.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new function.
+ */
+ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ isPartial = bitmask & 16,
+ isPartialRight = bitmask & 32;
+
+ if (!isBindKey && !isFunction(func)) {
+ throw new TypeError;
+ }
+ if (isPartial && !partialArgs.length) {
+ bitmask &= ~16;
+ isPartial = partialArgs = false;
+ }
+ if (isPartialRight && !partialRightArgs.length) {
+ bitmask &= ~32;
+ isPartialRight = partialRightArgs = false;
+ }
+ var bindData = func && func.__bindData__;
+ if (bindData && bindData !== true) {
+ // clone `bindData`
+ bindData = slice(bindData);
+ if (bindData[2]) {
+ bindData[2] = slice(bindData[2]);
+ }
+ if (bindData[3]) {
+ bindData[3] = slice(bindData[3]);
+ }
+ // set `thisBinding` is not previously bound
+ if (isBind && !(bindData[1] & 1)) {
+ bindData[4] = thisArg;
+ }
+ // set if previously bound but not currently (subsequent curried functions)
+ if (!isBind && bindData[1] & 1) {
+ bitmask |= 8;
+ }
+ // set curried arity if not yet set
+ if (isCurry && !(bindData[1] & 4)) {
+ bindData[5] = arity;
+ }
+ // append partial left arguments
+ if (isPartial) {
+ push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+ }
+ // append partial right arguments
+ if (isPartialRight) {
+ unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+ }
+ // merge flags
+ bindData[1] |= bitmask;
+ return createWrapper.apply(null, bindData);
+ }
+ // fast path for `_.bind`
+ var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+ return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+ }
+
+ /**
+ * Used by `escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(match) {
+ return htmlEscapes[match];
+ }
+
+ /**
+ * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+ * customized, this method returns the custom method, otherwise it returns
+ * the `baseIndexOf` function.
+ *
+ * @private
+ * @returns {Function} Returns the "indexOf" function.
+ */
+ function getIndexOf() {
+ var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a native function.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+ */
+ function isNative(value) {
+ return typeof value == 'function' && reNative.test(value);
+ }
+
+ /**
+ * Sets `this` binding data on a given function.
+ *
+ * @private
+ * @param {Function} func The function to set data on.
+ * @param {Array} value The data array to set.
+ */
+ var setBindData = !defineProperty ? noop : function(func, value) {
+ descriptor.value = value;
+ defineProperty(func, '__bindData__', descriptor);
+ descriptor.value = null;
+ };
+
+ /**
+ * A fallback implementation of `isPlainObject` which checks if a given value
+ * is an object created by the `Object` constructor, assuming objects created
+ * by the `Object` constructor have no inherited enumerable properties and that
+ * there are no `Object.prototype` extensions.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ */
+ function shimIsPlainObject(value) {
+ var ctor,
+ result;
+
+ // avoid non Object objects, `arguments` objects, and DOM elements
+ if (!(value && toString.call(value) == objectClass) ||
+ (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
+ return false;
+ }
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ forIn(value, function(value, key) {
+ result = key;
+ });
+ return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+ }
+
+ /**
+ * Used by `unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} match The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ function unescapeHtmlChar(match) {
+ return htmlUnescapes[match];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if `value` is an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })(1, 2, 3);
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == argsClass || false;
+ }
+
+ /**
+ * Checks if `value` is an array.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+ * @example
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ */
+ var isArray = nativeIsArray || function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == arrayClass || false;
+ };
+
+ /**
+ * A fallback implementation of `Object.keys` which produces an array of the
+ * given object's own enumerable property names.
+ *
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ */
+ var shimKeys = function(object) {
+ var index, iterable = object, result = [];
+ if (!iterable) return result;
+ if (!(objectTypes[typeof object])) return result;
+ for (index in iterable) {
+ if (hasOwnProperty.call(iterable, index)) {
+ result.push(index);
+ }
+ }
+ return result
+ };
+
+ /**
+ * Creates an array composed of the own enumerable property names of an object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ * @example
+ *
+ * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+ */
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (!isObject(object)) {
+ return [];
+ }
+ return nativeKeys(object);
+ };
+
+ /**
+ * Used to convert characters to HTML entities:
+ *
+ * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+ * don't require escaping in HTML and have no special meaning unless they're part
+ * of a tag or an unquoted attribute value.
+ * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+ */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
+
+ /** Used to match HTML entities and HTML characters */
+ var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+ reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property assignments of previous
+ * sources. If a callback is provided it will be executed to produce the
+ * assigned values. The callback is bound to `thisArg` and invoked with two
+ * arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @alias extend
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize assigning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+ * // => { 'name': 'fred', 'employer': 'slate' }
+ *
+ * var defaults = _.partialRight(_.assign, function(a, b) {
+ * return typeof a == 'undefined' ? b : a;
+ * });
+ *
+ * var object = { 'name': 'barney' };
+ * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var assign = function(object, source, guard) {
+ var index, iterable = object, result = iterable;
+ if (!iterable) return result;
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = typeof guard == 'number' ? 2 : args.length;
+ if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
+ var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
+ } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
+ callback = args[--argsLength];
+ }
+ while (++argsIndex < argsLength) {
+ iterable = args[argsIndex];
+ if (iterable && objectTypes[typeof iterable]) {
+ var ownIndex = -1,
+ ownProps = objectTypes[typeof iterable] && keys(iterable),
+ length = ownProps ? ownProps.length : 0;
+
+ while (++ownIndex < length) {
+ index = ownProps[ownIndex];
+ result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
+ }
+ }
+ }
+ return result
+ };
+
+ /**
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+ * be cloned, otherwise they will be assigned by reference. If a callback
+ * is provided it will be executed to produce the cloned values. If the
+ * callback returns `undefined` cloning will be handled by the method instead.
+ * The callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var shallow = _.clone(characters);
+ * shallow[0] === characters[0];
+ * // => true
+ *
+ * var deep = _.clone(characters, true);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * _.mixin({
+ * 'clone': _.partialRight(_.clone, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * })
+ * });
+ *
+ * var clone = _.clone(document.body);
+ * clone.childNodes.length;
+ * // => 0
+ */
+ function clone(value, isDeep, callback, thisArg) {
+ // allows working with "Collections" methods without using their `index`
+ // and `collection` arguments for `isDeep` and `callback`
+ if (typeof isDeep != 'boolean' && isDeep != null) {
+ thisArg = callback;
+ callback = isDeep;
+ isDeep = false;
+ }
+ return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates a deep clone of `value`. If a callback is provided it will be
+ * executed to produce the cloned values. If the callback returns `undefined`
+ * cloning will be handled by the method instead. The callback is bound to
+ * `thisArg` and invoked with one argument; (value).
+ *
+ * Note: This method is loosely based on the structured clone algorithm. Functions
+ * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+ * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+ * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the deep cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var deep = _.cloneDeep(characters);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'node': element
+ * };
+ *
+ * var clone = _.cloneDeep(view, function(value) {
+ * return _.isElement(value) ? value.cloneNode(true) : undefined;
+ * });
+ *
+ * clone.node == view.node;
+ * // => false
+ */
+ function cloneDeep(value, callback, thisArg) {
+ return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates an object that inherits from the given `prototype` object. If a
+ * `properties` object is provided its own enumerable properties are assigned
+ * to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties ? assign(result, properties) : result;
+ }
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property will be ignored.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param- {Object} [guard] Allows working with `_.reduce` without using its
+ * `key` and `object` arguments as sources.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var object = { 'name': 'barney' };
+ * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var defaults = function(object, source, guard) {
+ var index, iterable = object, result = iterable;
+ if (!iterable) return result;
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = typeof guard == 'number' ? 2 : args.length;
+ while (++argsIndex < argsLength) {
+ iterable = args[argsIndex];
+ if (iterable && objectTypes[typeof iterable]) {
+ var ownIndex = -1,
+ ownProps = objectTypes[typeof iterable] && keys(iterable),
+ length = ownProps ? ownProps.length : 0;
+
+ while (++ownIndex < length) {
+ index = ownProps[ownIndex];
+ if (typeof result[index] == 'undefined') result[index] = iterable[index];
+ }
+ }
+ }
+ return result
+ };
+
+ /**
+ * This method is like `_.findIndex` except that it returns the key of the
+ * first element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': false },
+ * 'fred': { 'age': 40, 'blocked': true },
+ * 'pebbles': { 'age': 1, 'blocked': false }
+ * };
+ *
+ * _.findKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => 'barney' (property order is not guaranteed across environments)
+ *
+ * // using "_.where" callback shorthand
+ * _.findKey(characters, { 'age': 1 });
+ * // => 'pebbles'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findKey(characters, 'blocked');
+ * // => 'fred'
+ */
+ function findKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * This method is like `_.findKey` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': true },
+ * 'fred': { 'age': 40, 'blocked': false },
+ * 'pebbles': { 'age': 1, 'blocked': true }
+ * };
+ *
+ * _.findLastKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastKey(characters, { 'age': 40 });
+ * // => 'fred'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastKey(characters, 'blocked');
+ * // => 'pebbles'
+ */
+ function findLastKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwnRight(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over own and inherited enumerable properties of an object,
+ * executing the callback for each property. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, key, object). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forIn(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+ */
+ var forIn = function(collection, callback, thisArg) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ for (index in iterable) {
+ if (callback(iterable[index], index, collection) === false) return result;
+ }
+ return result
+ };
+
+ /**
+ * This method is like `_.forIn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forInRight(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+ */
+ function forInRight(object, callback, thisArg) {
+ var pairs = [];
+
+ forIn(object, function(value, key) {
+ pairs.push(key, value);
+ });
+
+ var length = pairs.length;
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(pairs[length--], pairs[length], object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Iterates over own enumerable properties of an object, executing the callback
+ * for each property. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, key, object). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+ */
+ var forOwn = function(collection, callback, thisArg) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ var ownIndex = -1,
+ ownProps = objectTypes[typeof iterable] && keys(iterable),
+ length = ownProps ? ownProps.length : 0;
+
+ while (++ownIndex < length) {
+ index = ownProps[ownIndex];
+ if (callback(iterable[index], index, collection) === false) return result;
+ }
+ return result
+ };
+
+ /**
+ * This method is like `_.forOwn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+ */
+ function forOwnRight(object, callback, thisArg) {
+ var props = keys(object),
+ length = props.length;
+
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ var key = props[length];
+ if (callback(object[key], key, object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Creates a sorted array of property names of all enumerable properties,
+ * own and inherited, of `object` that have function values.
+ *
+ * @static
+ * @memberOf _
+ * @alias methods
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names that have function values.
+ * @example
+ *
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+ */
+ function functions(object) {
+ var result = [];
+ forIn(object, function(value, key) {
+ if (isFunction(value)) {
+ result.push(key);
+ }
+ });
+ return result.sort();
+ }
+
+ /**
+ * Checks if the specified property name exists as a direct property of `object`,
+ * instead of an inherited property.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to check.
+ * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+ * @example
+ *
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
+ */
+ function has(object, key) {
+ return object ? hasOwnProperty.call(object, key) : false;
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of the given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the created inverted object.
+ * @example
+ *
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
+ */
+ function invert(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ result[object[key]] = key;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a boolean value.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+ * @example
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass || false;
+ }
+
+ /**
+ * Checks if `value` is a date.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ */
+ function isDate(value) {
+ return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+ }
+
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ */
+ function isElement(value) {
+ return value && value.nodeType === 1 || false;
+ }
+
+ /**
+ * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+ * length of `0` and objects with no own enumerable properties are considered
+ * "empty".
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object|string} value The value to inspect.
+ * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({});
+ * // => true
+ *
+ * _.isEmpty('');
+ * // => true
+ */
+ function isEmpty(value) {
+ var result = true;
+ if (!value) {
+ return result;
+ }
+ var className = toString.call(value),
+ length = value.length;
+
+ if ((className == arrayClass || className == stringClass || className == argsClass ) ||
+ (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+ return !length;
+ }
+ forOwn(value, function() {
+ return (result = false);
+ });
+ return result;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent to each other. If a callback is provided it will be executed
+ * to compare values. If the callback returns `undefined` comparisons will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (a, b).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var copy = { 'name': 'fred' };
+ *
+ * object == copy;
+ * // => false
+ *
+ * _.isEqual(object, copy);
+ * // => true
+ *
+ * var words = ['hello', 'goodbye'];
+ * var otherWords = ['hi', 'goodbye'];
+ *
+ * _.isEqual(words, otherWords, function(a, b) {
+ * var reGreet = /^(?:hello|hi)$/i,
+ * aGreet = _.isString(a) && reGreet.test(a),
+ * bGreet = _.isString(b) && reGreet.test(b);
+ *
+ * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * });
+ * // => true
+ */
+ function isEqual(a, b, callback, thisArg) {
+ return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+ }
+
+ /**
+ * Checks if `value` is, or can be coerced to, a finite number.
+ *
+ * Note: This is not the same as native `isFinite` which will return true for
+ * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+ }
+
+ /**
+ * Checks if `value` is a function.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ */
+ function isFunction(value) {
+ return typeof value == 'function';
+ }
+
+ /**
+ * Checks if `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.io/#x8
+ // and avoid a V8 bug
+ // http://code.google.com/p/v8/issues/detail?id=2291
+ return !!(value && objectTypes[typeof value]);
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * Note: This is not the same as native `isNaN` which will return `true` for
+ * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // `NaN` as a primitive is the only value that is not equal to itself
+ // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(undefined);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is a number.
+ *
+ * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4 * 5);
+ * // => true
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ value && typeof value == 'object' && toString.call(value) == numberClass || false;
+ }
+
+ /**
+ * Checks if `value` is an object created by the `Object` constructor.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * _.isPlainObject(new Shape);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ */
+ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && toString.call(value) == objectClass)) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+ };
+
+ /**
+ * Checks if `value` is a regular expression.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+ * @example
+ *
+ * _.isRegExp(/fred/);
+ * // => true
+ */
+ function isRegExp(value) {
+ return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
+ }
+
+ /**
+ * Checks if `value` is a string.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('fred');
+ * // => true
+ */
+ function isString(value) {
+ return typeof value == 'string' ||
+ value && typeof value == 'object' && toString.call(value) == stringClass || false;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Creates an object with the same keys as `object` and values generated by
+ * running each own enumerable property of `object` through the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+ * @example
+ *
+ * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ *
+ * var characters = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.mapValues(characters, 'age');
+ * // => { 'fred': 40, 'pebbles': 1 }
+ */
+ function mapValues(object, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ forOwn(object, function(value, key, object) {
+ result[key] = callback(value, key, object);
+ });
+ return result;
+ }
+
+ /**
+ * Recursively merges own enumerable properties of the source object(s), that
+ * don't resolve to `undefined` into the destination object. Subsequent sources
+ * will overwrite property assignments of previous sources. If a callback is
+ * provided it will be executed to produce the merged values of the destination
+ * and source properties. If the callback returns `undefined` merging will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var names = {
+ * 'characters': [
+ * { 'name': 'barney' },
+ * { 'name': 'fred' }
+ * ]
+ * };
+ *
+ * var ages = {
+ * 'characters': [
+ * { 'age': 36 },
+ * { 'age': 40 }
+ * ]
+ * };
+ *
+ * _.merge(names, ages);
+ * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+ *
+ * var food = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var otherFood = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(food, otherFood, function(a, b) {
+ * return _.isArray(a) ? a.concat(b) : undefined;
+ * });
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+ */
+ function merge(object) {
+ var args = arguments,
+ length = 2;
+
+ if (!isObject(object)) {
+ return object;
+ }
+ // allows working with `_.reduce` and `_.reduceRight` without using
+ // their `index` and `collection` arguments
+ if (typeof args[2] != 'number') {
+ length = args.length;
+ }
+ if (length > 3 && typeof args[length - 2] == 'function') {
+ var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+ } else if (length > 2 && typeof args[length - 1] == 'function') {
+ callback = args[--length];
+ }
+ var sources = slice(arguments, 1, length),
+ index = -1,
+ stackA = getArray(),
+ stackB = getArray();
+
+ while (++index < length) {
+ baseMerge(object, sources[index], callback, stackA, stackB);
+ }
+ releaseArray(stackA);
+ releaseArray(stackB);
+ return object;
+ }
+
+ /**
+ * Creates a shallow clone of `object` excluding the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` omitting the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The properties to omit or the
+ * function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object without the omitted properties.
+ * @example
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+ * // => { 'name': 'fred' }
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function omit(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var props = [];
+ forIn(object, function(value, key) {
+ props.push(key);
+ });
+ props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ result[key] = object[key];
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (!callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates a two dimensional array of an object's key-value pairs,
+ * i.e. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a shallow clone of `object` composed of the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` picking the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The function called per
+ * iteration or property names to pick, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
+ * @example
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+ * // => { 'name': 'fred' }
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function pick(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var index = -1,
+ props = baseFlatten(arguments, true, false, 1),
+ length = isObject(object) ? props.length : 0;
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * An alternative to `_.reduce` this method transforms `object` to a new
+ * `accumulator` object which is the result of running each of its own
+ * enumerable properties through a callback, with each callback execution
+ * potentially mutating the `accumulator` object. The callback is bound to
+ * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+ * Callbacks may exit iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] The custom accumulator value.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+ * num *= num;
+ * if (num % 2) {
+ * return result.push(num) < 3;
+ * }
+ * });
+ * // => [1, 9, 25]
+ *
+ * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function transform(object, callback, accumulator, thisArg) {
+ var isArr = isArray(object);
+ if (accumulator == null) {
+ if (isArr) {
+ accumulator = [];
+ } else {
+ var ctor = object && object.constructor,
+ proto = ctor && ctor.prototype;
+
+ accumulator = baseCreate(proto);
+ }
+ }
+ if (callback) {
+ callback = lodash.createCallback(callback, thisArg, 4);
+ (isArr ? forEach : forOwn)(object, function(value, index, object) {
+ return callback(accumulator, value, index, object);
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * Creates an array composed of the own enumerable property values of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property values.
+ * @example
+ *
+ * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => [1, 2, 3] (property order is not guaranteed across environments)
+ */
+ function values(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of elements from the specified indexes, or keys, of the
+ * `collection`. Indexes may be specified as individual arguments or as arrays
+ * of indexes.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+ * to retrieve, specified as individual indexes or arrays of indexes.
+ * @returns {Array} Returns a new array of elements corresponding to the
+ * provided indexes.
+ * @example
+ *
+ * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+ * // => ['a', 'c', 'e']
+ *
+ * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+ * // => ['fred', 'pebbles']
+ */
+ function at(collection) {
+ var args = arguments,
+ index = -1,
+ props = baseFlatten(args, true, false, 1),
+ length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+ result = Array(length);
+
+ while(++index < length) {
+ result[index] = collection[props[index]];
+ }
+ return result;
+ }
+
+ /**
+ * Checks if a given value is present in a collection using strict equality
+ * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+ * offset from the end of the collection.
+ *
+ * @static
+ * @memberOf _
+ * @alias include
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {*} target The value to check for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+ * @example
+ *
+ * _.contains([1, 2, 3], 1);
+ * // => true
+ *
+ * _.contains([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+ * // => true
+ *
+ * _.contains('pebbles', 'eb');
+ * // => true
+ */
+ function contains(collection, target, fromIndex) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = collection ? collection.length : 0,
+ result = false;
+
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+ if (isArray(collection)) {
+ result = indexOf(collection, target, fromIndex) > -1;
+ } else if (typeof length == 'number') {
+ result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+ } else {
+ forOwn(collection, function(value) {
+ if (++index >= fromIndex) {
+ return !(result = value === target);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through the callback. The corresponding value
+ * of each key is the number of times the key was returned by the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+ });
+
+ /**
+ * Checks if the given callback returns truey value for **all** elements of
+ * a collection. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if all elements passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes']);
+ * // => false
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(characters, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(characters, { 'age': 36 });
+ * // => false
+ */
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return (result = !!callback(value, index, collection));
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning an array of all elements
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that passed the callback check.
+ * @example
+ *
+ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [2, 4, 6]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.filter(characters, 'blocked');
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ */
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning the first element that
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect, findWhere
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.find(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+ *
+ * // using "_.where" callback shorthand
+ * _.find(characters, { 'age': 1 });
+ * // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(characters, 'blocked');
+ * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+ */
+ function find(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ return value;
+ }
+ }
+ } else {
+ var result;
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+ }
+
+ /**
+ * This method is like `_.find` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * _.findLast([1, 2, 3, 4], function(num) {
+ * return num % 2 == 1;
+ * });
+ * // => 3
+ */
+ function findLast(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forEachRight(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, executing the callback for each
+ * element. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * Note: As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+ * // => logs each number and returns '1,2,3'
+ *
+ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+ * // => logs each number and returns the object (property order is not guaranteed across environments)
+ */
+ function forEach(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (callback(collection[index], index, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, callback);
+ }
+ return collection;
+ }
+
+ /**
+ * This method is like `_.forEach` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias eachRight
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+ * // => logs each number from right to left and returns '3,2,1'
+ */
+ function forEachRight(collection, callback, thisArg) {
+ var length = collection ? collection.length : 0;
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ while (length--) {
+ if (callback(collection[length], length, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ var props = keys(collection);
+ length = props.length;
+ forOwn(collection, function(value, key, collection) {
+ key = props ? props[--length] : --length;
+ return callback(collection[key], key, collection);
+ });
+ }
+ return collection;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of a collection through the callback. The corresponding value
+ * of each key is an array of the elements responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+ });
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of the collection through the given callback. The corresponding
+ * value of each key is the last element responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var keys = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.indexBy(keys, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ */
+ var indexBy = createAggregator(function(result, value, key) {
+ result[key] = value;
+ });
+
+ /**
+ * Invokes the method named by `methodName` on each element in the `collection`
+ * returning an array of the results of each invoked method. Additional arguments
+ * will be provided to each invoked method. If `methodName` is a function it
+ * will be invoked for, and `this` bound to, each element in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [arg] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of the results of each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ var args = slice(arguments, 2),
+ index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+ });
+ return result;
+ }
+
+ /**
+ * Creates an array of values by running each element in the collection
+ * through the callback. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @example
+ *
+ * _.map([1, 2, 3], function(num) { return num * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+ * // => [3, 6, 9] (property order is not guaranteed across environments)
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ var result = Array(length);
+ while (++index < length) {
+ result[index] = callback(collection[index], index, collection);
+ }
+ } else {
+ result = [];
+ forOwn(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the maximum value of a collection. If the collection is empty or
+ * falsey `-Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.max(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'fred', 'age': 40 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.max(characters, 'age');
+ * // => { 'name': 'fred', 'age': 40 };
+ */
+ function max(collection, callback, thisArg) {
+ var computed = -Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ forEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current > computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the minimum value of a collection. If the collection is empty or
+ * falsey `Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.min(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'barney', 'age': 36 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.min(characters, 'age');
+ * // => { 'name': 'barney', 'age': 36 };
+ */
+ function min(collection, callback, thisArg) {
+ var computed = Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ forEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current < computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the value of a specified property from all elements in the collection.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {string} property The name of the property to pluck.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.pluck(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ var pluck = map;
+
+ /**
+ * Reduces a collection to a value which is the accumulated result of running
+ * each element in the collection through the callback, where each successive
+ * callback execution consumes the return value of the previous execution. If
+ * `accumulator` is not provided the first element of the collection will be
+ * used as the initial `accumulator` value. The callback is bound to `thisArg`
+ * and invoked with four arguments; (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function reduce(collection, callback, accumulator, thisArg) {
+ if (!collection) return accumulator;
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+
+ var index = -1,
+ length = collection.length;
+
+ if (typeof length == 'number') {
+ if (noaccum) {
+ accumulator = collection[++index];
+ }
+ while (++index < length) {
+ accumulator = callback(accumulator, collection[index], index, collection);
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection)
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * This method is like `_.reduce` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var list = [[0, 1], [2, 3], [4, 5]];
+ * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+ forEachRight(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The opposite of `_.filter` this method returns the elements of a
+ * collection that the callback does **not** return truey for.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that failed the callback check.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [1, 3, 5]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.reject(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ *
+ * // using "_.where" callback shorthand
+ * _.reject(characters, { 'age': 36 });
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ */
+ function reject(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ return filter(collection, function(value, index, collection) {
+ return !callback(value, index, collection);
+ });
+ }
+
+ /**
+ * Retrieves a random element or `n` random elements from a collection.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to sample.
+ * @param {number} [n] The number of elements to sample.
+ * @param- {Object} [guard] Allows working with functions like `_.map`
+ * without using their `index` arguments as `n`.
+ * @returns {Array} Returns the random sample(s) of `collection`.
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ *
+ * _.sample([1, 2, 3, 4], 2);
+ * // => [3, 1]
+ */
+ function sample(collection, n, guard) {
+ if (collection && typeof collection.length != 'number') {
+ collection = values(collection);
+ }
+ if (n == null || guard) {
+ return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+ }
+ var result = shuffle(collection);
+ result.length = nativeMin(nativeMax(0, n), result.length);
+ return result;
+ }
+
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(collection) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ var rand = baseRandom(0, ++index);
+ result[index] = result[rand];
+ result[rand] = value;
+ });
+ return result;
+ }
+
+ /**
+ * Gets the size of the `collection` by returning `collection.length` for arrays
+ * and array-like objects or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns `collection.length` or number of own enumerable properties.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return typeof length == 'number' ? length : keys(collection).length;
+ }
+
+ /**
+ * Checks if the callback returns a truey value for **any** element of a
+ * collection. The function returns as soon as it finds a passing value and
+ * does not iterate over the entire collection. The callback is bound to
+ * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if any element passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.some(characters, 'blocked');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.some(characters, { 'age': 1 });
+ * // => false
+ */
+ function some(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if ((result = callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return !(result = callback(value, index, collection));
+ });
+ }
+ return !!result;
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through the callback. This method
+ * performs a stable sort, that is, it will preserve the original sort order
+ * of equal elements. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an array of property names is provided for `callback` the collection
+ * will be sorted by each property value.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of sorted elements.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+ * // => [3, 1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 26 },
+ * { 'name': 'fred', 'age': 30 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(_.sortBy(characters, 'age'), _.values);
+ * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+ *
+ * // sorting by multiple properties
+ * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+ * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+ */
+ function sortBy(collection, callback, thisArg) {
+ var index = -1,
+ isArr = isArray(callback),
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ if (!isArr) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ forEach(collection, function(value, key, collection) {
+ var object = result[++index] = getObject();
+ if (isArr) {
+ object.criteria = map(callback, function(key) { return value[key]; });
+ } else {
+ (object.criteria = getArray())[0] = callback(value, key, collection);
+ }
+ object.index = index;
+ object.value = value;
+ });
+
+ length = result.length;
+ result.sort(compareAscending);
+ while (length--) {
+ var object = result[length];
+ result[length] = object.value;
+ if (!isArr) {
+ releaseArray(object.criteria);
+ }
+ releaseObject(object);
+ }
+ return result;
+ }
+
+ /**
+ * Converts the `collection` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to convert.
+ * @returns {Array} Returns the new converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+ * // => [2, 3, 4]
+ */
+ function toArray(collection) {
+ if (collection && typeof collection.length == 'number') {
+ return slice(collection);
+ }
+ return values(collection);
+ }
+
+ /**
+ * Performs a deep comparison of each element in a `collection` to the given
+ * `properties` object, returning an array of all elements that have equivalent
+ * property values.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Object} props The object of property values to filter by.
+ * @returns {Array} Returns a new array of elements that have the given properties.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * _.where(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+ *
+ * _.where(characters, { 'pets': ['dino'] });
+ * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+ */
+ var where = filter;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are all falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all values of the provided arrays using strict
+ * equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {...Array} [values] The arrays of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ * // => [1, 3, 4]
+ */
+ function difference(array) {
+ return baseDifference(array, baseFlatten(arguments, true, true, 1));
+ }
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.findIndex(characters, function(chr) {
+ * return chr.age < 20;
+ * });
+ * // => 2
+ *
+ * // using "_.where" callback shorthand
+ * _.findIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findIndex(characters, 'blocked');
+ * // => 1
+ */
+ function findIndex(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ if (callback(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': true },
+ * { 'name': 'fred', 'age': 40, 'blocked': false },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': true }
+ * ];
+ *
+ * _.findLastIndex(characters, function(chr) {
+ * return chr.age > 30;
+ * });
+ * // => 1
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastIndex(characters, 'blocked');
+ * // => 2
+ */
+ function findLastIndex(array, callback, thisArg) {
+ var length = array ? array.length : 0;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(array[length], length, array)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the first element or first `n` elements of an array. If a callback
+ * is provided elements at the beginning of the array are returned as long
+ * as the callback returns truey. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head, take
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the first element(s) of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.first([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.first(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function first(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = -1;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[0] : undefined;
+ }
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, n), length));
+ }
+
+ /**
+ * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+ * is truey, the array will only be flattened a single level. If a callback
+ * is provided each element of the array is passed through the callback before
+ * flattening. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ *
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.flatten(characters, 'pets');
+ * // => ['hoppy', 'baby puss', 'dino']
+ */
+ function flatten(array, isShallow, callback, thisArg) {
+ // juggle arguments
+ if (typeof isShallow != 'boolean' && isShallow != null) {
+ thisArg = callback;
+ callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+ isShallow = false;
+ }
+ if (callback != null) {
+ array = map(array, callback, thisArg);
+ }
+ return baseFlatten(array, isShallow);
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found using
+ * strict equality for comparisons, i.e. `===`. If the array is already sorted
+ * providing `true` for `fromIndex` will run a faster binary search.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ if (typeof fromIndex == 'number') {
+ var length = array ? array.length : 0;
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+ } else if (fromIndex) {
+ var index = sortedIndex(array, value);
+ return array[index] === value ? index : -1;
+ }
+ return baseIndexOf(array, value, fromIndex);
+ }
+
+ /**
+ * Gets all but the last element or last `n` elements of an array. If a
+ * callback is provided elements at the end of the array are excluded from
+ * the result as long as the callback returns truey. The callback is bound
+ * to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.initial([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.initial([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.initial(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function initial(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : callback || n;
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+ }
+
+ /**
+ * Creates an array of unique values present in all provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of shared values.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection() {
+ var args = [],
+ argsIndex = -1,
+ argsLength = arguments.length,
+ caches = getArray(),
+ indexOf = getIndexOf(),
+ trustIndexOf = indexOf === baseIndexOf,
+ seen = getArray();
+
+ while (++argsIndex < argsLength) {
+ var value = arguments[argsIndex];
+ if (isArray(value) || isArguments(value)) {
+ args.push(value);
+ caches.push(trustIndexOf && value.length >= largeArraySize &&
+ createCache(argsIndex ? args[argsIndex] : seen));
+ }
+ }
+ var array = args[0],
+ index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ outer:
+ while (++index < length) {
+ var cache = caches[0];
+ value = array[index];
+
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+ argsIndex = argsLength;
+ (cache || seen).push(value);
+ while (--argsIndex) {
+ cache = caches[argsIndex];
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ }
+ while (argsLength--) {
+ cache = caches[argsLength];
+ if (cache) {
+ releaseObject(cache);
+ }
+ }
+ releaseArray(caches);
+ releaseArray(seen);
+ return result;
+ }
+
+ /**
+ * Gets the last element or last `n` elements of an array. If a callback is
+ * provided elements at the end of the array are returned as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the last element(s) of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ *
+ * _.last([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.last([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.last(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.last(characters, { 'employer': 'na' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function last(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[length - 1] : undefined;
+ }
+ }
+ return slice(array, nativeMax(0, length - n));
+ }
+
+ /**
+ * Gets the index at which the last occurrence of `value` is found using strict
+ * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+ * as the offset from the end of the collection.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var index = array ? array.length : 0;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all provided values from the given array using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {...*} [value] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3, 1, 2, 3];
+ * _.pull(array, 2, 3);
+ * console.log(array);
+ * // => [1, 1]
+ */
+ function pull(array) {
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = args.length,
+ length = array ? array.length : 0;
+
+ while (++argsIndex < argsLength) {
+ var index = -1,
+ value = args[argsIndex];
+ while (++index < length) {
+ if (array[index] === value) {
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`. If `start` is less than `stop` a
+ * zero-length range is created unless a negative `step` is specified.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns a new range array.
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = typeof step == 'number' ? step : (+step || 1);
+
+ if (end == null) {
+ end = start;
+ start = 0;
+ }
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(0, ceil((end - start) / (step || 1))),
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
+ }
+
+ /**
+ * Removes all elements from an array that the callback returns truey for
+ * and returns an array of removed elements. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4, 5, 6];
+ * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+ *
+ * console.log(array);
+ * // => [1, 3, 5]
+ *
+ * console.log(evens);
+ * // => [2, 4, 6]
+ */
+ function remove(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ var value = array[index];
+ if (callback(value, index, array)) {
+ result.push(value);
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The opposite of `_.initial` this method gets all but the first element or
+ * first `n` elements of an array. If a callback function is provided elements
+ * at the beginning of the array are excluded from the result as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias drop, tail
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.rest([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.rest([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.rest(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.rest(characters, { 'employer': 'slate' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function rest(array, callback, thisArg) {
+ if (typeof callback != 'number' && callback != null) {
+ var n = 0,
+ index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+ }
+ return slice(array, n);
+ }
+
+ /**
+ * Uses a binary search to determine the smallest index at which a value
+ * should be inserted into a given sorted array in order to maintain the sort
+ * order of the array. If a callback is provided it will be executed for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([20, 30, 50], 40);
+ * // => 2
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 2
+ *
+ * var dict = {
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * };
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return dict.wordToNumber[word];
+ * });
+ * // => 2
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return this.wordToNumber[word];
+ * }, dict);
+ * // => 2
+ */
+ function sortedIndex(array, value, callback, thisArg) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ // explicitly reference `identity` for better inlining in Firefox
+ callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+ value = callback(value);
+
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ (callback(array[mid]) < value)
+ ? low = mid + 1
+ : high = mid;
+ }
+ return low;
+ }
+
+ /**
+ * Creates an array of unique values, in order, of the provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of combined values.
+ * @example
+ *
+ * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2, 3, 5, 4]
+ */
+ function union() {
+ return baseUniq(baseFlatten(arguments, true, true));
+ }
+
+ /**
+ * Creates a duplicate-value-free version of an array using strict equality
+ * for comparisons, i.e. `===`. If the array is sorted, providing
+ * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+ * each element of `array` is passed through the callback before uniqueness
+ * is computed. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1, 3, 1]);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 1, 2, 2, 3], true);
+ * // => [1, 2, 3]
+ *
+ * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+ * // => ['A', 'b', 'C']
+ *
+ * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+ * // => [1, 2.5, 3]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, callback, thisArg) {
+ // juggle arguments
+ if (typeof isSorted != 'boolean' && isSorted != null) {
+ thisArg = callback;
+ callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+ isSorted = false;
+ }
+ if (callback != null) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ return baseUniq(array, isSorted, callback);
+ }
+
+ /**
+ * Creates an array excluding all provided values using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to filter.
+ * @param {...*} [value] The values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ return baseDifference(array, slice(arguments, 1));
+ }
+
+ /**
+ * Creates an array that is the symmetric difference of the provided arrays.
+ * See http://en.wikipedia.org/wiki/Symmetric_difference.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of values.
+ * @example
+ *
+ * _.xor([1, 2, 3], [5, 2, 1, 4]);
+ * // => [3, 5, 4]
+ *
+ * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+ * // => [1, 4, 5]
+ */
+ function xor() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var array = arguments[index];
+ if (isArray(array) || isArguments(array)) {
+ var result = result
+ ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+ : array;
+ }
+ }
+ return result || [];
+ }
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the first
+ * elements of the given arrays, the second of which contains the second
+ * elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @alias unzip
+ * @category Arrays
+ * @param {...Array} [array] Arrays to process.
+ * @returns {Array} Returns a new array of grouped elements.
+ * @example
+ *
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ */
+ function zip() {
+ var array = arguments.length > 1 ? arguments : arguments[0],
+ index = -1,
+ length = array ? max(pluck(array, 'length')) : 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = pluck(array, index);
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed from arrays of `keys` and `values`. Provide
+ * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+ * or two arrays, one of `keys` and one of corresponding `values`.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Arrays
+ * @param {Array} keys The array of keys.
+ * @param {Array} [values=[]] The array of values.
+ * @returns {Object} Returns an object composed of the given keys and
+ * corresponding values.
+ * @example
+ *
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ function zipObject(keys, values) {
+ var index = -1,
+ length = keys ? keys.length : 0,
+ result = {};
+
+ if (!values && length && !isArray(keys[0])) {
+ values = [];
+ }
+ while (++index < length) {
+ var key = keys[index];
+ if (values) {
+ result[key] = values[index];
+ } else if (key) {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that executes `func`, with the `this` binding and
+ * arguments of the created function, only after being called `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {number} n The number of times the function must be called before
+ * `func` is executed.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('Done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => logs 'Done saving!', after all saves have completed
+ */
+ function after(n, func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with the `this`
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * provided to the bound function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var func = function(greeting) {
+ * return greeting + ' ' + this.name;
+ * };
+ *
+ * func = _.bind(func, { 'name': 'fred' }, 'hi');
+ * func();
+ * // => 'hi fred'
+ */
+ function bind(func, thisArg) {
+ return arguments.length > 2
+ ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+ : createWrapper(func, 1, null, null, thisArg);
+ }
+
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing
+ * method. Method names may be specified as individual arguments or as arrays
+ * of method names. If no method names are provided all the function properties
+ * of `object` will be bound.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {...string} [methodName] The object method names to
+ * bind, specified as individual method names or arrays of method names.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { console.log('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => logs 'clicked docs', when the button is clicked
+ */
+ function bindAll(object) {
+ var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+ index = -1,
+ length = funcs.length;
+
+ while (++index < length) {
+ var key = funcs[index];
+ object[key] = createWrapper(object[key], 1, null, null, object);
+ }
+ return object;
+ }
+
+ /**
+ * Creates a function that, when called, invokes the method at `object[key]`
+ * and prepends any additional `bindKey` arguments to those provided to the bound
+ * function. This method differs from `_.bind` by allowing bound functions to
+ * reference methods that will be redefined or don't yet exist.
+ * See http://michaux.ca/articles/lazy-function-definition-pattern.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object the method belongs to.
+ * @param {string} key The key of the method.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'name': 'fred',
+ * 'greet': function(greeting) {
+ * return greeting + ' ' + this.name;
+ * }
+ * };
+ *
+ * var func = _.bindKey(object, 'greet', 'hi');
+ * func();
+ * // => 'hi fred'
+ *
+ * object.greet = function(greeting) {
+ * return greeting + 'ya ' + this.name + '!';
+ * };
+ *
+ * func();
+ * // => 'hiya fred!'
+ */
+ function bindKey(object, key) {
+ return arguments.length > 2
+ ? createWrapper(key, 19, slice(arguments, 2), null, object)
+ : createWrapper(key, 3, null, null, object);
+ }
+
+ /**
+ * Creates a function that is the composition of the provided functions,
+ * where each function consumes the return value of the function that follows.
+ * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * Each function is executed with the `this` binding of the composed function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {...Function} [func] Functions to compose.
+ * @returns {Function} Returns the new composed function.
+ * @example
+ *
+ * var realNameMap = {
+ * 'pebbles': 'penelope'
+ * };
+ *
+ * var format = function(name) {
+ * name = realNameMap[name.toLowerCase()] || name;
+ * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+ * };
+ *
+ * var greet = function(formatted) {
+ * return 'Hiya ' + formatted + '!';
+ * };
+ *
+ * var welcome = _.compose(greet, format);
+ * welcome('pebbles');
+ * // => 'Hiya Penelope!'
+ */
+ function compose() {
+ var funcs = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ if (!isFunction(funcs[length])) {
+ throw new TypeError;
+ }
+ }
+ return function() {
+ var args = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ args = [funcs[length].apply(this, args)];
+ }
+ return args[0];
+ };
+ }
+
+ /**
+ * Creates a function which accepts one or more arguments of `func` that when
+ * invoked either executes `func` returning its result, if all `func` arguments
+ * have been provided, or returns a function that accepts one or more of the
+ * remaining `func` arguments, and so on. The arity of `func` can be specified
+ * if `func.length` is not sufficient.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var curried = _.curry(function(a, b, c) {
+ * console.log(a + b + c);
+ * });
+ *
+ * curried(1)(2)(3);
+ * // => 6
+ *
+ * curried(1, 2)(3);
+ * // => 6
+ *
+ * curried(1, 2, 3);
+ * // => 6
+ */
+ function curry(func, arity) {
+ arity = typeof arity == 'number' ? arity : (+arity || func.length);
+ return createWrapper(func, 4, null, null, null, arity);
+ }
+
+ /**
+ * Creates a function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked.
+ * Provide an options object to indicate that `func` should be invoked on
+ * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+ * to the debounced function will return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the debounced function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to delay.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+ * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // avoid costly calculations while the window size is in flux
+ * var lazyLayout = _.debounce(calculateLayout, 150);
+ * jQuery(window).on('resize', lazyLayout);
+ *
+ * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+ * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * });
+ *
+ * // ensure `batchLog` is executed once after 1 second of debounced calls
+ * var source = new EventSource('/stream');
+ * source.addEventListener('message', _.debounce(batchLog, 250, {
+ * 'maxWait': 1000
+ * }, false);
+ */
+ function debounce(func, wait, options) {
+ var args,
+ maxTimeoutId,
+ result,
+ stamp,
+ thisArg,
+ timeoutId,
+ trailingCall,
+ lastCalled = 0,
+ maxWait = false,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ wait = nativeMax(0, wait) || 0;
+ if (options === true) {
+ var leading = true;
+ trailing = false;
+ } else if (isObject(options)) {
+ leading = options.leading;
+ maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ var delayed = function() {
+ var remaining = wait - (now() - stamp);
+ if (remaining <= 0) {
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ var isCalled = trailingCall;
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (isCalled) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ } else {
+ timeoutId = setTimeout(delayed, remaining);
+ }
+ };
+
+ var maxDelayed = function() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (trailing || (maxWait !== wait)) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ };
+
+ return function() {
+ args = arguments;
+ stamp = now();
+ thisArg = this;
+ trailingCall = trailing && (timeoutId || !leading);
+
+ if (maxWait === false) {
+ var leadingCall = leading && !timeoutId;
+ } else {
+ if (!maxTimeoutId && !leading) {
+ lastCalled = stamp;
+ }
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0;
+
+ if (isCalled) {
+ if (maxTimeoutId) {
+ maxTimeoutId = clearTimeout(maxTimeoutId);
+ }
+ lastCalled = stamp;
+ result = func.apply(thisArg, args);
+ }
+ else if (!maxTimeoutId) {
+ maxTimeoutId = setTimeout(maxDelayed, remaining);
+ }
+ }
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
+ timeoutId = setTimeout(delayed, wait);
+ }
+ if (leadingCall) {
+ isCalled = true;
+ result = func.apply(thisArg, args);
+ }
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Defers executing the `func` function until the current call stack has cleared.
+ * Additional arguments will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to defer.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) { console.log(text); }, 'deferred');
+ * // logs 'deferred' after one or more milliseconds
+ */
+ function defer(func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 1);
+ return setTimeout(function() { func.apply(undefined, args); }, 1);
+ }
+
+ /**
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay execution.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) { console.log(text); }, 1000, 'later');
+ * // => logs 'later' after one second
+ */
+ function delay(func, wait) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 2);
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided it will be used to determine the cache key for storing the result
+ * based on the arguments provided to the memoized function. By default, the
+ * first argument provided to the memoized function is used as the cache key.
+ * The `func` is executed with the `this` binding of the memoized function.
+ * The result cache is exposed as the `cache` property on the memoized function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] A function used to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var fibonacci = _.memoize(function(n) {
+ * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ * });
+ *
+ * fibonacci(9)
+ * // => 34
+ *
+ * var data = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // modifying the result cache
+ * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+ * get('pebbles');
+ * // => { 'name': 'pebbles', 'age': 1 }
+ *
+ * get.cache.pebbles.name = 'penelope';
+ * get('pebbles');
+ * // => { 'name': 'penelope', 'age': 1 }
+ */
+ function memoize(func, resolver) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var memoized = function() {
+ var cache = memoized.cache,
+ key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+ return hasOwnProperty.call(cache, key)
+ ? cache[key]
+ : (cache[key] = func.apply(this, arguments));
+ }
+ memoized.cache = {};
+ return memoized;
+ }
+
+ /**
+ * Creates a function that is restricted to execute `func` once. Repeat calls to
+ * the function will return the value of the first call. The `func` is executed
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` executes `createApplication` once
+ */
+ function once(func) {
+ var ran,
+ result;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (ran) {
+ return result;
+ }
+ ran = true;
+ result = func.apply(this, arguments);
+
+ // clear the `func` variable so the function may be garbage collected
+ func = null;
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those provided to the new function. This
+ * method is similar to `_.bind` except it does **not** alter the `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ' ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('fred');
+ * // => 'hi fred'
+ */
+ function partial(func) {
+ return createWrapper(func, 16, slice(arguments, 1));
+ }
+
+ /**
+ * This method is like `_.partial` except that `partial` arguments are
+ * appended to those provided to the new function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+ *
+ * var options = {
+ * 'variable': 'data',
+ * 'imports': { 'jq': $ }
+ * };
+ *
+ * defaultsDeep(options, _.templateSettings);
+ *
+ * options.variable
+ * // => 'data'
+ *
+ * options.imports
+ * // => { '_': _, 'jq': $ }
+ */
+ function partialRight(func) {
+ return createWrapper(func, 32, null, slice(arguments, 1));
+ }
+
+ /**
+ * Creates a function that, when executed, will only call the `func` function
+ * at most once per every `wait` milliseconds. Provide an options object to
+ * indicate that `func` should be invoked on the leading and/or trailing edge
+ * of the `wait` timeout. Subsequent calls to the throttled function will
+ * return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the throttled function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to throttle.
+ * @param {number} wait The number of milliseconds to throttle executions to.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // avoid excessively updating the position while scrolling
+ * var throttled = _.throttle(updatePosition, 100);
+ * jQuery(window).on('scroll', throttled);
+ *
+ * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+ * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+ * 'trailing': false
+ * }));
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ if (options === false) {
+ leading = false;
+ } else if (isObject(options)) {
+ leading = 'leading' in options ? options.leading : leading;
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ debounceOptions.leading = leading;
+ debounceOptions.maxWait = wait;
+ debounceOptions.trailing = trailing;
+
+ return debounce(func, wait, debounceOptions);
+ }
+
+ /**
+ * Creates a function that provides `value` to the wrapper function as its
+ * first argument. Additional arguments provided to the function are appended
+ * to those provided to the wrapper function. The wrapper is executed with
+ * the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {*} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '<p>' + func(text) + '</p>';
+ * });
+ *
+ * p('Fred, Wilma, & Pebbles');
+ * // => '<p>Fred, Wilma, & Pebbles</p>'
+ */
+ function wrap(value, wrapper) {
+ return createWrapper(wrapper, 16, [value]);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that returns `value`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value The value to return from the new function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var getter = _.constant(object);
+ * getter() === object;
+ * // => true
+ */
+ function constant(value) {
+ return function() {
+ return value;
+ };
+ }
+
+ /**
+ * Produces a callback bound to an optional `thisArg`. If `func` is a property
+ * name the created callback will return the property value for a given element.
+ * If `func` is an object the created callback will return `true` for elements
+ * that contain the equivalent object properties, otherwise it will return `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // wrap to create custom callback shorthands
+ * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+ * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+ * return !match ? func(callback, thisArg) : function(object) {
+ * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+ * };
+ * });
+ *
+ * _.filter(characters, 'age__gt38');
+ * // => [{ 'name': 'fred', 'age': 40 }]
+ */
+ function createCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (func == null || type == 'function') {
+ return baseCreateCallback(func, thisArg, argCount);
+ }
+ // handle "_.pluck" style callback shorthands
+ if (type != 'object') {
+ return property(func);
+ }
+ var props = keys(func),
+ key = props[0],
+ a = func[key];
+
+ // handle "_.where" style callback shorthands
+ if (props.length == 1 && a === a && !isObject(a)) {
+ // fast path the common case of providing an object with a single
+ // property containing a primitive value
+ return function(object) {
+ var b = object[key];
+ return a === b && (a !== 0 || (1 / a == 1 / b));
+ };
+ }
+ return function(object) {
+ var length = props.length,
+ result = false;
+
+ while (length--) {
+ if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+ break;
+ }
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding HTML entities.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles'
+ */
+ function escape(string) {
+ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+ }
+
+ /**
+ * This method returns the first argument provided to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.identity(object) === object;
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
+
+ /**
+ * Adds function properties of a source object to the destination object.
+ * If `object` is a function methods will be added to its prototype as well.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Function|Object} [object=lodash] object The destination object.
+ * @param {Object} source The object of functions to add.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+ * @example
+ *
+ * function capitalize(string) {
+ * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+ * }
+ *
+ * _.mixin({ 'capitalize': capitalize });
+ * _.capitalize('fred');
+ * // => 'Fred'
+ *
+ * _('fred').capitalize().value();
+ * // => 'Fred'
+ *
+ * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+ * _('fred').capitalize();
+ * // => 'Fred'
+ */
+ function mixin(object, source, options) {
+ var chain = true,
+ methodNames = source && functions(source);
+
+ if (!source || (!options && !methodNames.length)) {
+ if (options == null) {
+ options = source;
+ }
+ ctor = lodashWrapper;
+ source = object;
+ object = lodash;
+ methodNames = functions(source);
+ }
+ if (options === false) {
+ chain = false;
+ } else if (isObject(options) && 'chain' in options) {
+ chain = options.chain;
+ }
+ var ctor = object,
+ isFunc = isFunction(ctor);
+
+ forEach(methodNames, function(methodName) {
+ var func = object[methodName] = source[methodName];
+ if (isFunc) {
+ ctor.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ value = this.__wrapped__,
+ args = [value];
+
+ push.apply(args, arguments);
+ var result = func.apply(object, args);
+ if (chain || chainAll) {
+ if (value === result && isObject(result)) {
+ return this;
+ }
+ result = new ctor(result);
+ result.__chain__ = chainAll;
+ }
+ return result;
+ };
+ }
+ });
+ }
+
+ /**
+ * Reverts the '_' variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ context._ = oldDash;
+ return this;
+ }
+
+ /**
+ * A no-operation function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.noop(object) === undefined;
+ * // => true
+ */
+ function noop() {
+ // no operation performed
+ }
+
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var stamp = _.now();
+ * _.defer(function() { console.log(_.now() - stamp); });
+ * // => logs the number of milliseconds it took for the deferred function to be called
+ */
+ var now = isNative(now = Date.now) && now || function() {
+ return new Date().getTime();
+ };
+
+ /**
+ * Converts the given value into an integer of the specified radix.
+ * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+ * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+ *
+ * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+ * implementations. See http://es5.github.io/#E.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} value The value to parse.
+ * @param {number} [radix] The radix used to interpret the value to parse.
+ * @returns {number} Returns the new integer value.
+ * @example
+ *
+ * _.parseInt('08');
+ * // => 8
+ */
+ var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+ // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+ return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+ };
+
+ /**
+ * Creates a "_.pluck" style function, which returns the `key` value of a
+ * given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} key The name of the property to retrieve.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 36 }
+ * ];
+ *
+ * var getName = _.property('name');
+ *
+ * _.map(characters, getName);
+ * // => ['barney', 'fred']
+ *
+ * _.sortBy(characters, getName);
+ * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
+ */
+ function property(key) {
+ return function(object) {
+ return object[key];
+ };
+ }
+
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is provided a number between `0` and the given number will be
+ * returned. If `floating` is truey or either `min` or `max` are floats a
+ * floating-point number will be returned instead of an integer.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} [min=0] The minimum possible value.
+ * @param {number} [max=1] The maximum possible value.
+ * @param {boolean} [floating=false] Specify returning a floating-point number.
+ * @returns {number} Returns a random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(min, max, floating) {
+ var noMin = min == null,
+ noMax = max == null;
+
+ if (floating == null) {
+ if (typeof min == 'boolean' && noMax) {
+ floating = min;
+ min = 1;
+ }
+ else if (!noMax && typeof max == 'boolean') {
+ floating = max;
+ noMax = true;
+ }
+ }
+ if (noMin && noMax) {
+ max = 1;
+ }
+ min = +min || 0;
+ if (noMax) {
+ max = min;
+ min = 0;
+ } else {
+ max = +max || 0;
+ }
+ if (floating || min % 1 || max % 1) {
+ var rand = nativeRandom();
+ return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+ }
+ return baseRandom(min, max);
+ }
+
+ /**
+ * Resolves the value of property `key` on `object`. If `key` is a function
+ * it will be invoked with the `this` binding of `object` and its result returned,
+ * else the property value is returned. If `object` is falsey then `undefined`
+ * is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to resolve.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = {
+ * 'cheese': 'crumpets',
+ * 'stuff': function() {
+ * return 'nonsense';
+ * }
+ * };
+ *
+ * _.result(object, 'cheese');
+ * // => 'crumpets'
+ *
+ * _.result(object, 'stuff');
+ * // => 'nonsense'
+ */
+ function result(object, key) {
+ if (object) {
+ var value = object[key];
+ return isFunction(value) ? object[key]() : value;
+ }
+ }
+
+ /**
+ * A micro-templating method that handles arbitrary delimiters, preserves
+ * whitespace, and correctly escapes quotes within interpolated code.
+ *
+ * Note: In the development build, `_.template` utilizes sourceURLs for easier
+ * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ *
+ * For more information on precompiling templates see:
+ * https://lodash.com/custom-builds
+ *
+ * For more information on Chrome extension sandboxes see:
+ * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} text The template text.
+ * @param {Object} data The data object used to populate the text.
+ * @param {Object} [options] The options object.
+ * @param {RegExp} [options.escape] The "escape" delimiter.
+ * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+ * @param {Object} [options.imports] An object to import into the template as local variables.
+ * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+ * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [variable] The data object variable name.
+ * @returns {Function|string} Returns a compiled function when no `data` object
+ * is given, else it returns the interpolated text.
+ * @example
+ *
+ * // using the "interpolate" delimiter to create a compiled template
+ * var compiled = _.template('hello <%= name %>');
+ * compiled({ 'name': 'fred' });
+ * // => 'hello fred'
+ *
+ * // using the "escape" delimiter to escape HTML in data property values
+ * _.template('<b><%- value %></b>', { 'value': '<script>' });
+ * // => '<b><script></b>'
+ *
+ * // using the "evaluate" delimiter to generate HTML
+ * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+ * _.template('hello ${ name }', { 'name': 'pebbles' });
+ * // => 'hello pebbles'
+ *
+ * // using the internal `print` function in "evaluate" delimiters
+ * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+ * // => 'hello barney!'
+ *
+ * // using a custom template delimiters
+ * _.templateSettings = {
+ * 'interpolate': /{{([\s\S]+?)}}/g
+ * };
+ *
+ * _.template('hello {{ name }}!', { 'name': 'mustache' });
+ * // => 'hello mustache!'
+ *
+ * // using the `imports` option to import jQuery
+ * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the `sourceURL` option to specify a custom sourceURL for the template
+ * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+ * compiled(data);
+ * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+ *
+ * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+ * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+ * compiled.source;
+ * // => function(data) {
+ * var __t, __p = '', __e = _.escape;
+ * __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+ * return __p;
+ * }
+ *
+ * // using the `source` property to inline compiled templates for meaningful
+ * // line numbers in error messages and a stack trace
+ * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+ * var JST = {\
+ * "main": ' + _.template(mainText).source + '\
+ * };\
+ * ');
+ */
+ function template(text, data, options) {
+ // based on John Resig's `tmpl` implementation
+ // http://ejohn.org/blog/javascript-micro-templating/
+ // and Laura Doktorova's doT.js
+ // https://github.com/olado/doT
+ var settings = lodash.templateSettings;
+ text = String(text || '');
+
+ // avoid missing dependencies when `iteratorTemplate` is not defined
+ options = defaults({}, options, settings);
+
+ var imports = defaults({}, options.imports, settings.imports),
+ importsKeys = keys(imports),
+ importsValues = values(imports);
+
+ var isEvaluating,
+ index = 0,
+ interpolate = options.interpolate || reNoMatch,
+ source = "__p += '";
+
+ // compile the regexp to match each delimiter
+ var reDelimiters = RegExp(
+ (options.escape || reNoMatch).source + '|' +
+ interpolate.source + '|' +
+ (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+ (options.evaluate || reNoMatch).source + '|$'
+ , 'g');
+
+ text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+ interpolateValue || (interpolateValue = esTemplateValue);
+
+ // escape characters that cannot be included in string literals
+ source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+ // replace delimiters with snippets
+ if (escapeValue) {
+ source += "' +\n__e(" + escapeValue + ") +\n'";
+ }
+ if (evaluateValue) {
+ isEvaluating = true;
+ source += "';\n" + evaluateValue + ";\n__p += '";
+ }
+ if (interpolateValue) {
+ source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+ }
+ index = offset + match.length;
+
+ // the JS engine embedded in Adobe products requires returning the `match`
+ // string in order to produce the correct `offset` value
+ return match;
+ });
+
+ source += "';\n";
+
+ // if `variable` is not specified, wrap a with-statement around the generated
+ // code to add the data object to the top of the scope chain
+ var variable = options.variable,
+ hasVariable = variable;
+
+ if (!hasVariable) {
+ variable = 'obj';
+ source = 'with (' + variable + ') {\n' + source + '\n}\n';
+ }
+ // cleanup code by stripping empty strings
+ source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+ .replace(reEmptyStringMiddle, '$1')
+ .replace(reEmptyStringTrailing, '$1;');
+
+ // frame code as the function body
+ source = 'function(' + variable + ') {\n' +
+ (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
+ "var __t, __p = '', __e = _.escape" +
+ (isEvaluating
+ ? ', __j = Array.prototype.join;\n' +
+ "function print() { __p += __j.call(arguments, '') }\n"
+ : ';\n'
+ ) +
+ source +
+ 'return __p\n}';
+
+ // Use a sourceURL for easier debugging.
+ // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
+
+ try {
+ var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
+ } catch(e) {
+ e.source = source;
+ throw e;
+ }
+ if (data) {
+ return result(data);
+ }
+ // provide the compiled function's source by its `toString` method, in
+ // supported environments, or the `source` property as a convenience for
+ // inlining compiled templates during the build process
+ result.source = source;
+ return result;
+ }
+
+ /**
+ * Executes the callback `n` times, returning an array of the results
+ * of each callback execution. The callback is bound to `thisArg` and invoked
+ * with one argument; (index).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} n The number of times to execute the callback.
+ * @param {Function} callback The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns an array of the results of each `callback` execution.
+ * @example
+ *
+ * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+ * // => [3, 6, 4]
+ *
+ * _.times(3, function(n) { mage.castSpell(n); });
+ * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+ *
+ * _.times(3, function(n) { this.cast(n); }, mage);
+ * // => also calls `mage.castSpell(n)` three times
+ */
+ function times(n, callback, thisArg) {
+ n = (n = +n) > -1 ? n : 0;
+ var index = -1,
+ result = Array(n);
+
+ callback = baseCreateCallback(callback, thisArg, 1);
+ while (++index < n) {
+ result[index] = callback(index);
+ }
+ return result;
+ }
+
+ /**
+ * The inverse of `_.escape` this method converts the HTML entities
+ * `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding characters.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to unescape.
+ * @returns {string} Returns the unescaped string.
+ * @example
+ *
+ * _.unescape('Fred, Barney & Pebbles');
+ * // => 'Fred, Barney & Pebbles'
+ */
+ function unescape(string) {
+ return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+ }
+
+ /**
+ * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} [prefix] The value to prefix the ID with.
+ * @returns {string} Returns the unique ID.
+ * @example
+ *
+ * _.uniqueId('contact_');
+ * // => 'contact_104'
+ *
+ * _.uniqueId();
+ * // => '105'
+ */
+ function uniqueId(prefix) {
+ var id = ++idCounter;
+ return String(prefix == null ? '' : prefix) + id;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object that wraps the given value with explicit
+ * method chaining enabled.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _.chain(characters)
+ * .sortBy('age')
+ * .map(function(chr) { return chr.name + ' is ' + chr.age; })
+ * .first()
+ * .value();
+ * // => 'pebbles is 1'
+ */
+ function chain(value) {
+ value = new lodashWrapper(value);
+ value.__chain__ = true;
+ return value;
+ }
+
+ /**
+ * Invokes `interceptor` with the `value` as the first argument and then
+ * returns `value`. The purpose of this method is to "tap into" a method
+ * chain in order to perform operations on intermediate results within
+ * the chain.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3, 4])
+ * .tap(function(array) { array.pop(); })
+ * .reverse()
+ * .value();
+ * // => [3, 2, 1]
+ */
+ function tap(value, interceptor) {
+ interceptor(value);
+ return value;
+ }
+
+ /**
+ * Enables explicit method chaining on the wrapper object.
+ *
+ * @name chain
+ * @memberOf _
+ * @category Chaining
+ * @returns {*} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // without explicit chaining
+ * _(characters).first();
+ * // => { 'name': 'barney', 'age': 36 }
+ *
+ * // with explicit chaining
+ * _(characters).chain()
+ * .first()
+ * .pick('age')
+ * .value();
+ * // => { 'age': 36 }
+ */
+ function wrapperChain() {
+ this.__chain__ = true;
+ return this;
+ }
+
+ /**
+ * Produces the `toString` result of the wrapped value.
+ *
+ * @name toString
+ * @memberOf _
+ * @category Chaining
+ * @returns {string} Returns the string result.
+ * @example
+ *
+ * _([1, 2, 3]).toString();
+ * // => '1,2,3'
+ */
+ function wrapperToString() {
+ return String(this.__wrapped__);
+ }
+
+ /**
+ * Extracts the wrapped value.
+ *
+ * @name valueOf
+ * @memberOf _
+ * @alias value
+ * @category Chaining
+ * @returns {*} Returns the wrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).valueOf();
+ * // => [1, 2, 3]
+ */
+ function wrapperValueOf() {
+ return this.__wrapped__;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return wrapped values when chaining
+ lodash.after = after;
+ lodash.assign = assign;
+ lodash.at = at;
+ lodash.bind = bind;
+ lodash.bindAll = bindAll;
+ lodash.bindKey = bindKey;
+ lodash.chain = chain;
+ lodash.compact = compact;
+ lodash.compose = compose;
+ lodash.constant = constant;
+ lodash.countBy = countBy;
+ lodash.create = create;
+ lodash.createCallback = createCallback;
+ lodash.curry = curry;
+ lodash.debounce = debounce;
+ lodash.defaults = defaults;
+ lodash.defer = defer;
+ lodash.delay = delay;
+ lodash.difference = difference;
+ lodash.filter = filter;
+ lodash.flatten = flatten;
+ lodash.forEach = forEach;
+ lodash.forEachRight = forEachRight;
+ lodash.forIn = forIn;
+ lodash.forInRight = forInRight;
+ lodash.forOwn = forOwn;
+ lodash.forOwnRight = forOwnRight;
+ lodash.functions = functions;
+ lodash.groupBy = groupBy;
+ lodash.indexBy = indexBy;
+ lodash.initial = initial;
+ lodash.intersection = intersection;
+ lodash.invert = invert;
+ lodash.invoke = invoke;
+ lodash.keys = keys;
+ lodash.map = map;
+ lodash.mapValues = mapValues;
+ lodash.max = max;
+ lodash.memoize = memoize;
+ lodash.merge = merge;
+ lodash.min = min;
+ lodash.omit = omit;
+ lodash.once = once;
+ lodash.pairs = pairs;
+ lodash.partial = partial;
+ lodash.partialRight = partialRight;
+ lodash.pick = pick;
+ lodash.pluck = pluck;
+ lodash.property = property;
+ lodash.pull = pull;
+ lodash.range = range;
+ lodash.reject = reject;
+ lodash.remove = remove;
+ lodash.rest = rest;
+ lodash.shuffle = shuffle;
+ lodash.sortBy = sortBy;
+ lodash.tap = tap;
+ lodash.throttle = throttle;
+ lodash.times = times;
+ lodash.toArray = toArray;
+ lodash.transform = transform;
+ lodash.union = union;
+ lodash.uniq = uniq;
+ lodash.values = values;
+ lodash.where = where;
+ lodash.without = without;
+ lodash.wrap = wrap;
+ lodash.xor = xor;
+ lodash.zip = zip;
+ lodash.zipObject = zipObject;
+
+ // add aliases
+ lodash.collect = map;
+ lodash.drop = rest;
+ lodash.each = forEach;
+ lodash.eachRight = forEachRight;
+ lodash.extend = assign;
+ lodash.methods = functions;
+ lodash.object = zipObject;
+ lodash.select = filter;
+ lodash.tail = rest;
+ lodash.unique = uniq;
+ lodash.unzip = zip;
+
+ // add functions to `lodash.prototype`
+ mixin(lodash);
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return unwrapped values when chaining
+ lodash.clone = clone;
+ lodash.cloneDeep = cloneDeep;
+ lodash.contains = contains;
+ lodash.escape = escape;
+ lodash.every = every;
+ lodash.find = find;
+ lodash.findIndex = findIndex;
+ lodash.findKey = findKey;
+ lodash.findLast = findLast;
+ lodash.findLastIndex = findLastIndex;
+ lodash.findLastKey = findLastKey;
+ lodash.has = has;
+ lodash.identity = identity;
+ lodash.indexOf = indexOf;
+ lodash.isArguments = isArguments;
+ lodash.isArray = isArray;
+ lodash.isBoolean = isBoolean;
+ lodash.isDate = isDate;
+ lodash.isElement = isElement;
+ lodash.isEmpty = isEmpty;
+ lodash.isEqual = isEqual;
+ lodash.isFinite = isFinite;
+ lodash.isFunction = isFunction;
+ lodash.isNaN = isNaN;
+ lodash.isNull = isNull;
+ lodash.isNumber = isNumber;
+ lodash.isObject = isObject;
+ lodash.isPlainObject = isPlainObject;
+ lodash.isRegExp = isRegExp;
+ lodash.isString = isString;
+ lodash.isUndefined = isUndefined;
+ lodash.lastIndexOf = lastIndexOf;
+ lodash.mixin = mixin;
+ lodash.noConflict = noConflict;
+ lodash.noop = noop;
+ lodash.now = now;
+ lodash.parseInt = parseInt;
+ lodash.random = random;
+ lodash.reduce = reduce;
+ lodash.reduceRight = reduceRight;
+ lodash.result = result;
+ lodash.runInContext = runInContext;
+ lodash.size = size;
+ lodash.some = some;
+ lodash.sortedIndex = sortedIndex;
+ lodash.template = template;
+ lodash.unescape = unescape;
+ lodash.uniqueId = uniqueId;
+
+ // add aliases
+ lodash.all = every;
+ lodash.any = some;
+ lodash.detect = find;
+ lodash.findWhere = find;
+ lodash.foldl = reduce;
+ lodash.foldr = reduceRight;
+ lodash.include = contains;
+ lodash.inject = reduce;
+
+ mixin(function() {
+ var source = {}
+ forOwn(lodash, function(func, methodName) {
+ if (!lodash.prototype[methodName]) {
+ source[methodName] = func;
+ }
+ });
+ return source;
+ }(), false);
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions capable of returning wrapped and unwrapped values when chaining
+ lodash.first = first;
+ lodash.last = last;
+ lodash.sample = sample;
+
+ // add aliases
+ lodash.take = first;
+ lodash.head = first;
+
+ forOwn(lodash, function(func, methodName) {
+ var callbackable = methodName !== 'sample';
+ if (!lodash.prototype[methodName]) {
+ lodash.prototype[methodName]= function(n, guard) {
+ var chainAll = this.__chain__,
+ result = func(this.__wrapped__, n, guard);
+
+ return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
+ ? result
+ : new lodashWrapper(result, chainAll);
+ };
+ }
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf _
+ * @type string
+ */
+ lodash.VERSION = '2.4.2';
+
+ // add "Chaining" functions to the wrapper
+ lodash.prototype.chain = wrapperChain;
+ lodash.prototype.toString = wrapperToString;
+ lodash.prototype.value = wrapperValueOf;
+ lodash.prototype.valueOf = wrapperValueOf;
+
+ // add `Array` functions that return unwrapped values
+ forEach(['join', 'pop', 'shift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ result = func.apply(this.__wrapped__, arguments);
+
+ return chainAll
+ ? new lodashWrapper(result, chainAll)
+ : result;
+ };
+ });
+
+ // add `Array` functions that return the existing wrapped value
+ forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ func.apply(this.__wrapped__, arguments);
+ return this;
+ };
+ });
+
+ // add `Array` functions that return new wrapped values
+ forEach(['concat', 'slice', 'splice'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
+ };
+ });
+
+ return lodash;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose Lo-Dash
+ var _ = runInContext();
+
+ // some AMD build optimizers like r.js check for condition patterns like the following:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // Expose Lo-Dash to the global object even when an AMD loader is present in
+ // case Lo-Dash is loaded with a RequireJS shim config.
+ // See http://requirejs.org/docs/api.html#config-shim
+ root._ = _;
+
+ // define as an anonymous module so, through path mapping, it can be
+ // referenced as the "underscore" module
+ define(function() {
+ return _;
+ });
+ }
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ else if (freeExports && freeModule) {
+ // in Node.js or RingoJS
+ if (moduleExports) {
+ (freeModule.exports = _)._ = _;
+ }
+ // in Narwhal or Rhino -require
+ else {
+ freeExports._ = _;
+ }
+ }
+ else {
+ // in a browser or Rhino
+ root._ = _;
+ }
+}.call(this));
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE
+ * Build: `lodash modern -o ./dist/lodash.js`
+ */
+;(function(){function n(n,r,t){for(var e=(t||0)-1,u=n?n.length:0;++e<u;)if(n[e]===r)return e;return-1}function r(r,t){var e=typeof t;if(r=r.cache,"boolean"==e||null==t)return r[t]?0:-1;"number"!=e&&"string"!=e&&(e="object");var u="number"==e?t:m+t;return r=(r=r[e])&&r[u],"object"==e?r&&n(r,t)>-1?0:-1:r?0:-1}function t(n){var r=this.cache,t=typeof n;if("boolean"==t||null==n)r[n]=!0;else{"number"!=t&&"string"!=t&&(t="object");var e="number"==t?n:m+n,u=r[t]||(r[t]={});"object"==t?(u[e]||(u[e]=[])).push(n):u[e]=!0;
+
+}}function e(n){return n.charCodeAt(0)}function u(n,r){for(var t=n.criteria,e=r.criteria,u=-1,o=t.length;++u<o;){var a=t[u],i=e[u];if(a!==i){if(a>i||"undefined"==typeof a)return 1;if(a<i||"undefined"==typeof i)return-1}}return n.index-r.index}function o(n){var r=-1,e=n.length,u=n[0],o=n[e/2|0],a=n[e-1];if(u&&"object"==typeof u&&o&&"object"==typeof o&&a&&"object"==typeof a)return!1;var i=f();i["false"]=i["null"]=i["true"]=i.undefined=!1;var l=f();for(l.array=n,l.cache=i,l.push=t;++r<e;)l.push(n[r]);
+
+return l}function a(n){return"\\"+G[n]}function i(){return h.pop()||[]}function f(){return g.pop()||{array:null,cache:null,criteria:null,"false":!1,index:0,"null":!1,number:null,object:null,push:null,string:null,"true":!1,undefined:!1,value:null}}function l(n){n.length=0,h.length<_&&h.push(n)}function c(n){var r=n.cache;r&&c(r),n.array=n.cache=n.criteria=n.object=n.number=n.string=n.value=null,g.length<_&&g.push(n)}function p(n,r,t){r||(r=0),"undefined"==typeof t&&(t=n?n.length:0);for(var e=-1,u=t-r||0,o=Array(u<0?0:u);++e<u;)o[e]=n[r+e];
+
+return o}function s(t){function h(n){return n&&"object"==typeof n&&!Yt(n)&&Tt.call(n,"__wrapped__")?n:new g(n)}function g(n,r){this.__chain__=!!r,this.__wrapped__=n}function _(n){function r(){if(e){var n=p(e);$t.apply(n,arguments)}if(this instanceof r){var o=J(t.prototype),a=t.apply(o,n||arguments);return Sn(a)?a:o}return t.apply(u,n||arguments)}var t=n[0],e=n[2],u=n[4];return Xt(r,n),r}function G(n,r,t,e,u){if(t){var o=t(n);if("undefined"!=typeof o)return o}var a=Sn(n);if(!a)return n;var f=Nt.call(n);
+
+if(!K[f])return n;var c=Jt[f];switch(f){case F:case B:return new c(+n);case q:case P:return new c(n);case L:return o=c(n.source,C.exec(n)),o.lastIndex=n.lastIndex,o}var s=Yt(n);if(r){var v=!e;e||(e=i()),u||(u=i());for(var h=e.length;h--;)if(e[h]==n)return u[h];o=s?c(n.length):{}}else o=s?p(n):oe({},n);return s&&(Tt.call(n,"index")&&(o.index=n.index),Tt.call(n,"input")&&(o.input=n.input)),r?(e.push(n),u.push(o),(s?Xn:fe)(n,function(n,a){o[a]=G(n,r,t,e,u)}),v&&(l(e),l(u)),o):o}function J(n,r){return Sn(n)?zt(n):{};
+
+}function Q(n,r,t){if("function"!=typeof n)return Yr;if("undefined"==typeof r||!("prototype"in n))return n;var e=n.__bindData__;if("undefined"==typeof e&&(Qt.funcNames&&(e=!n.name),e=e||!Qt.funcDecomp,!e)){var u=At.call(n);Qt.funcNames||(e=!O.test(u)),e||(e=I.test(u),Xt(n,e))}if(e===!1||e!==!0&&1&e[1])return n;switch(t){case 1:return function(t){return n.call(r,t)};case 2:return function(t,e){return n.call(r,t,e)};case 3:return function(t,e,u){return n.call(r,t,e,u)};case 4:return function(t,e,u,o){
+return n.call(r,t,e,u,o)}}return $r(n,r)}function X(n){function r(){var n=f?a:this;if(u){var h=p(u);$t.apply(h,arguments)}if((o||c)&&(h||(h=p(arguments)),o&&$t.apply(h,o),c&&h.length<i))return e|=16,X([t,s?e:-4&e,h,null,a,i]);if(h||(h=arguments),l&&(t=n[v]),this instanceof r){n=J(t.prototype);var g=t.apply(n,h);return Sn(g)?g:n}return t.apply(n,h)}var t=n[0],e=n[1],u=n[2],o=n[3],a=n[4],i=n[5],f=1&e,l=2&e,c=4&e,s=8&e,v=t;return Xt(r,n),r}function Y(t,e){var u=-1,a=ln(),i=t?t.length:0,f=i>=b&&a===n,l=[];
+
+if(f){var p=o(e);p?(a=r,e=p):f=!1}for(;++u<i;){var s=t[u];a(e,s)<0&&l.push(s)}return f&&c(e),l}function nn(n,r,t,e){for(var u=(e||0)-1,o=n?n.length:0,a=[];++u<o;){var i=n[u];if(i&&"object"==typeof i&&"number"==typeof i.length&&(Yt(i)||vn(i))){r||(i=nn(i,r,t));var f=-1,l=i.length,c=a.length;for(a.length+=l;++f<l;)a[c++]=i[f]}else t||a.push(i)}return a}function rn(n,r,t,e,u,o){if(t){var a=t(n,r);if("undefined"!=typeof a)return!!a}if(n===r)return 0!==n||1/n==1/r;var f=typeof n,c=typeof r;if(n===n&&(!n||!V[f])&&(!r||!V[c]))return!1;
+
+if(null==n||null==r)return n===r;var p=Nt.call(n),s=Nt.call(r);if(p==T&&(p=z),s==T&&(s=z),p!=s)return!1;switch(p){case F:case B:return+n==+r;case q:return n!=+n?r!=+r:0==n?1/n==1/r:n==+r;case L:case P:return n==jt(r)}var h=p==$;if(!h){var g=Tt.call(n,"__wrapped__"),y=Tt.call(r,"__wrapped__");if(g||y)return rn(g?n.__wrapped__:n,y?r.__wrapped__:r,t,e,u,o);if(p!=z)return!1;var m=n.constructor,b=r.constructor;if(m!=b&&!(In(m)&&m instanceof m&&In(b)&&b instanceof b)&&"constructor"in n&&"constructor"in r)return!1;
+
+}var _=!u;u||(u=i()),o||(o=i());for(var d=u.length;d--;)if(u[d]==n)return o[d]==r;var w=0;if(a=!0,u.push(n),o.push(r),h){if(d=n.length,w=r.length,a=w==d,a||e)for(;w--;){var j=d,k=r[w];if(e)for(;j--&&!(a=rn(n[j],k,t,e,u,o)););else if(!(a=rn(n[w],k,t,e,u,o)))break}}else ie(r,function(r,i,f){return Tt.call(f,i)?(w++,a=Tt.call(n,i)&&rn(n[i],r,t,e,u,o)):v}),a&&!e&&ie(n,function(n,r,t){return Tt.call(t,r)?a=--w>-1:v});return u.pop(),o.pop(),_&&(l(u),l(o)),a}function tn(n,r,t,e,u){(Yt(r)?Xn:fe)(r,function(r,o){
+var a,i,f=r,l=n[o];if(r&&((i=Yt(r))||le(r))){for(var c=e.length;c--;)if(a=e[c]==r){l=u[c];break}if(!a){var p;t&&(f=t(l,r),(p="undefined"!=typeof f)&&(l=f)),p||(l=i?Yt(l)?l:[]:le(l)?l:{}),e.push(r),u.push(l),p||tn(l,r,t,e,u)}}else t&&(f=t(l,r),"undefined"==typeof f&&(f=r)),"undefined"!=typeof f&&(l=f);n[o]=l})}function en(n,r){return n+St(Ht()*(r-n+1))}function un(t,e,u){var a=-1,f=ln(),p=t?t.length:0,s=[],v=!e&&p>=b&&f===n,h=u||v?i():s;if(v){var g=o(h);f=r,h=g}for(;++a<p;){var y=t[a],m=u?u(y,a,t):y;
+
+(e?!a||h[h.length-1]!==m:f(h,m)<0)&&((u||v)&&h.push(m),s.push(y))}return v?(l(h.array),c(h)):u&&l(h),s}function on(n){return function(r,t,e){var u={};t=h.createCallback(t,e,3);var o=-1,a=r?r.length:0;if("number"==typeof a)for(;++o<a;){var i=r[o];n(u,i,t(i,o,r),r)}else fe(r,function(r,e,o){n(u,r,t(r,e,o),o)});return u}}function an(n,r,t,e,u,o){var a=1&r,i=2&r,f=4&r,l=16&r,c=32&r;if(!i&&!In(n))throw new kt;l&&!t.length&&(r&=-17,l=t=!1),c&&!e.length&&(r&=-33,c=e=!1);var s=n&&n.__bindData__;if(s&&s!==!0)return s=p(s),
+s[2]&&(s[2]=p(s[2])),s[3]&&(s[3]=p(s[3])),!a||1&s[1]||(s[4]=u),!a&&1&s[1]&&(r|=8),!f||4&s[1]||(s[5]=o),l&&$t.apply(s[2]||(s[2]=[]),t),c&&Wt.apply(s[3]||(s[3]=[]),e),s[1]|=r,an.apply(null,s);var v=1==r||17===r?_:X;return v([n,r,t,e,u,o])}function fn(n){return re[n]}function ln(){var r=(r=h.indexOf)===br?n:r;return r}function cn(n){return"function"==typeof n&&Rt.test(n)}function pn(n){var r,t;return n&&Nt.call(n)==z&&(r=n.constructor,!In(r)||r instanceof r)?(ie(n,function(n,r){t=r}),"undefined"==typeof t||Tt.call(n,t)):!1;
+
+}function sn(n){return te[n]}function vn(n){return n&&"object"==typeof n&&"number"==typeof n.length&&Nt.call(n)==T||!1}function hn(n,r,t,e){return"boolean"!=typeof r&&null!=r&&(e=t,t=r,r=!1),G(n,r,"function"==typeof t&&Q(t,e,1))}function gn(n,r,t){return G(n,!0,"function"==typeof r&&Q(r,t,1))}function yn(n,r){var t=J(n);return r?oe(t,r):t}function mn(n,r,t){var e;return r=h.createCallback(r,t,3),fe(n,function(n,t,u){return r(n,t,u)?(e=t,!1):v}),e}function bn(n,r,t){var e;return r=h.createCallback(r,t,3),
+dn(n,function(n,t,u){return r(n,t,u)?(e=t,!1):v}),e}function _n(n,r,t){var e=[];ie(n,function(n,r){e.push(r,n)});var u=e.length;for(r=Q(r,t,3);u--&&r(e[u--],e[u],n)!==!1;);return n}function dn(n,r,t){var e=ne(n),u=e.length;for(r=Q(r,t,3);u--;){var o=e[u];if(r(n[o],o,n)===!1)break}return n}function wn(n){var r=[];return ie(n,function(n,t){In(n)&&r.push(t)}),r.sort()}function jn(n,r){return n?Tt.call(n,r):!1}function kn(n){for(var r=-1,t=ne(n),e=t.length,u={};++r<e;){var o=t[r];u[n[o]]=o}return u}function xn(n){
+return n===!0||n===!1||n&&"object"==typeof n&&Nt.call(n)==F||!1}function Cn(n){return n&&"object"==typeof n&&Nt.call(n)==B||!1}function On(n){return n&&1===n.nodeType||!1}function Nn(n){var r=!0;if(!n)return r;var t=Nt.call(n),e=n.length;return t==$||t==P||t==T||t==z&&"number"==typeof e&&In(n.splice)?!e:(fe(n,function(){return r=!1}),r)}function Rn(n,r,t,e){return rn(n,r,"function"==typeof t&&Q(t,e,2))}function En(n){return Pt(n)&&!Kt(parseFloat(n))}function In(n){return"function"==typeof n}function Sn(n){
+return!(!n||!V[typeof n])}function An(n){return Tn(n)&&n!=+n}function Dn(n){return null===n}function Tn(n){return"number"==typeof n||n&&"object"==typeof n&&Nt.call(n)==q||!1}function $n(n){return n&&"object"==typeof n&&Nt.call(n)==L||!1}function Fn(n){return"string"==typeof n||n&&"object"==typeof n&&Nt.call(n)==P||!1}function Bn(n){return"undefined"==typeof n}function Wn(n,r,t){var e={};return r=h.createCallback(r,t,3),fe(n,function(n,t,u){e[t]=r(n,t,u)}),e}function qn(n){var r=arguments,t=2;if(!Sn(n))return n;
+
+if("number"!=typeof r[2]&&(t=r.length),t>3&&"function"==typeof r[t-2])var e=Q(r[--t-1],r[t--],2);else t>2&&"function"==typeof r[t-1]&&(e=r[--t]);for(var u=p(arguments,1,t),o=-1,a=i(),f=i();++o<t;)tn(n,u[o],e,a,f);return l(a),l(f),n}function zn(n,r,t){var e={};if("function"!=typeof r){var u=[];ie(n,function(n,r){u.push(r)}),u=Y(u,nn(arguments,!0,!1,1));for(var o=-1,a=u.length;++o<a;){var i=u[o];e[i]=n[i]}}else r=h.createCallback(r,t,3),ie(n,function(n,t,u){r(n,t,u)||(e[t]=n)});return e}function Ln(n){
+for(var r=-1,t=ne(n),e=t.length,u=ht(e);++r<e;){var o=t[r];u[r]=[o,n[o]]}return u}function Pn(n,r,t){var e={};if("function"!=typeof r)for(var u=-1,o=nn(arguments,!0,!1,1),a=Sn(n)?o.length:0;++u<a;){var i=o[u];i in n&&(e[i]=n[i])}else r=h.createCallback(r,t,3),ie(n,function(n,t,u){r(n,t,u)&&(e[t]=n)});return e}function Kn(n,r,t,e){var u=Yt(n);if(null==t)if(u)t=[];else{var o=n&&n.constructor,a=o&&o.prototype;t=J(a)}return r&&(r=h.createCallback(r,e,4),(u?Xn:fe)(n,function(n,e,u){return r(t,n,e,u)})),
+t}function Un(n){for(var r=-1,t=ne(n),e=t.length,u=ht(e);++r<e;)u[r]=n[t[r]];return u}function Mn(n){for(var r=arguments,t=-1,e=nn(r,!0,!1,1),u=r[2]&&r[2][r[1]]===n?1:e.length,o=ht(u);++t<u;)o[t]=n[e[t]];return o}function Vn(n,r,t){var e=-1,u=ln(),o=n?n.length:0,a=!1;return t=(t<0?Mt(0,o+t):t)||0,Yt(n)?a=u(n,r,t)>-1:"number"==typeof o?a=(Fn(n)?n.indexOf(r,t):u(n,r,t))>-1:fe(n,function(n){return++e<t?v:!(a=n===r)}),a}function Gn(n,r,t){var e=!0;r=h.createCallback(r,t,3);var u=-1,o=n?n.length:0;if("number"==typeof o)for(;++u<o&&(e=!!r(n[u],u,n)););else fe(n,function(n,t,u){
+return e=!!r(n,t,u)});return e}function Hn(n,r,t){var e=[];r=h.createCallback(r,t,3);var u=-1,o=n?n.length:0;if("number"==typeof o)for(;++u<o;){var a=n[u];r(a,u,n)&&e.push(a)}else fe(n,function(n,t,u){r(n,t,u)&&e.push(n)});return e}function Jn(n,r,t){r=h.createCallback(r,t,3);var e=-1,u=n?n.length:0;if("number"!=typeof u){var o;return fe(n,function(n,t,e){return r(n,t,e)?(o=n,!1):v}),o}for(;++e<u;){var a=n[e];if(r(a,e,n))return a}}function Qn(n,r,t){var e;return r=h.createCallback(r,t,3),Yn(n,function(n,t,u){
+return r(n,t,u)?(e=n,!1):v}),e}function Xn(n,r,t){var e=-1,u=n?n.length:0;if(r=r&&"undefined"==typeof t?r:Q(r,t,3),"number"==typeof u)for(;++e<u&&r(n[e],e,n)!==!1;);else fe(n,r);return n}function Yn(n,r,t){var e=n?n.length:0;if(r=r&&"undefined"==typeof t?r:Q(r,t,3),"number"==typeof e)for(;e--&&r(n[e],e,n)!==!1;);else{var u=ne(n);e=u.length,fe(n,function(n,t,o){return t=u?u[--e]:--e,r(o[t],t,o)})}return n}function Zn(n,r){var t=p(arguments,2),e=-1,u="function"==typeof r,o=n?n.length:0,a=ht("number"==typeof o?o:0);
+
+return Xn(n,function(n){a[++e]=(u?r:n[r]).apply(n,t)}),a}function nr(n,r,t){var e=-1,u=n?n.length:0;if(r=h.createCallback(r,t,3),"number"==typeof u)for(var o=ht(u);++e<u;)o[e]=r(n[e],e,n);else o=[],fe(n,function(n,t,u){o[++e]=r(n,t,u)});return o}function rr(n,r,t){var u=-(1/0),o=u;if("function"!=typeof r&&t&&t[r]===n&&(r=null),null==r&&Yt(n))for(var a=-1,i=n.length;++a<i;){var f=n[a];f>o&&(o=f)}else r=null==r&&Fn(n)?e:h.createCallback(r,t,3),Xn(n,function(n,t,e){var a=r(n,t,e);a>u&&(u=a,o=n)});return o;
+
+}function tr(n,r,t){var u=1/0,o=u;if("function"!=typeof r&&t&&t[r]===n&&(r=null),null==r&&Yt(n))for(var a=-1,i=n.length;++a<i;){var f=n[a];f<o&&(o=f)}else r=null==r&&Fn(n)?e:h.createCallback(r,t,3),Xn(n,function(n,t,e){var a=r(n,t,e);a<u&&(u=a,o=n)});return o}function er(n,r,t,e){if(!n)return t;var u=arguments.length<3;r=h.createCallback(r,e,4);var o=-1,a=n.length;if("number"==typeof a)for(u&&(t=n[++o]);++o<a;)t=r(t,n[o],o,n);else fe(n,function(n,e,o){t=u?(u=!1,n):r(t,n,e,o)});return t}function ur(n,r,t,e){
+var u=arguments.length<3;return r=h.createCallback(r,e,4),Yn(n,function(n,e,o){t=u?(u=!1,n):r(t,n,e,o)}),t}function or(n,r,t){return r=h.createCallback(r,t,3),Hn(n,function(n,t,e){return!r(n,t,e)})}function ar(n,r,t){if(n&&"number"!=typeof n.length&&(n=Un(n)),null==r||t)return n?n[en(0,n.length-1)]:v;var e=ir(n);return e.length=Vt(Mt(0,r),e.length),e}function ir(n){var r=-1,t=n?n.length:0,e=ht("number"==typeof t?t:0);return Xn(n,function(n){var t=en(0,++r);e[r]=e[t],e[t]=n}),e}function fr(n){var r=n?n.length:0;
+
+return"number"==typeof r?r:ne(n).length}function lr(n,r,t){var e;r=h.createCallback(r,t,3);var u=-1,o=n?n.length:0;if("number"==typeof o)for(;++u<o&&!(e=r(n[u],u,n)););else fe(n,function(n,t,u){return!(e=r(n,t,u))});return!!e}function cr(n,r,t){var e=-1,o=Yt(r),a=n?n.length:0,p=ht("number"==typeof a?a:0);for(o||(r=h.createCallback(r,t,3)),Xn(n,function(n,t,u){var a=p[++e]=f();o?a.criteria=nr(r,function(r){return n[r]}):(a.criteria=i())[0]=r(n,t,u),a.index=e,a.value=n}),a=p.length,p.sort(u);a--;){
+var s=p[a];p[a]=s.value,o||l(s.criteria),c(s)}return p}function pr(n){return n&&"number"==typeof n.length?p(n):Un(n)}function sr(n){for(var r=-1,t=n?n.length:0,e=[];++r<t;){var u=n[r];u&&e.push(u)}return e}function vr(n){return Y(n,nn(arguments,!0,!0,1))}function hr(n,r,t){var e=-1,u=n?n.length:0;for(r=h.createCallback(r,t,3);++e<u;)if(r(n[e],e,n))return e;return-1}function gr(n,r,t){var e=n?n.length:0;for(r=h.createCallback(r,t,3);e--;)if(r(n[e],e,n))return e;return-1}function yr(n,r,t){var e=0,u=n?n.length:0;
+
+if("number"!=typeof r&&null!=r){var o=-1;for(r=h.createCallback(r,t,3);++o<u&&r(n[o],o,n);)e++}else if(e=r,null==e||t)return n?n[0]:v;return p(n,0,Vt(Mt(0,e),u))}function mr(n,r,t,e){return"boolean"!=typeof r&&null!=r&&(e=t,t="function"!=typeof r&&e&&e[r]===n?null:r,r=!1),null!=t&&(n=nr(n,t,e)),nn(n,r)}function br(r,t,e){if("number"==typeof e){var u=r?r.length:0;e=e<0?Mt(0,u+e):e||0}else if(e){var o=Nr(r,t);return r[o]===t?o:-1}return n(r,t,e)}function _r(n,r,t){var e=0,u=n?n.length:0;if("number"!=typeof r&&null!=r){
+var o=u;for(r=h.createCallback(r,t,3);o--&&r(n[o],o,n);)e++}else e=null==r||t?1:r||e;return p(n,0,Vt(Mt(0,u-e),u))}function dr(){for(var t=[],e=-1,u=arguments.length,a=i(),f=ln(),p=f===n,s=i();++e<u;){var v=arguments[e];(Yt(v)||vn(v))&&(t.push(v),a.push(p&&v.length>=b&&o(e?t[e]:s)))}var h=t[0],g=-1,y=h?h.length:0,m=[];n:for(;++g<y;){var _=a[0];if(v=h[g],(_?r(_,v):f(s,v))<0){for(e=u,(_||s).push(v);--e;)if(_=a[e],(_?r(_,v):f(t[e],v))<0)continue n;m.push(v)}}for(;u--;)_=a[u],_&&c(_);return l(a),l(s),
+m}function wr(n,r,t){var e=0,u=n?n.length:0;if("number"!=typeof r&&null!=r){var o=u;for(r=h.createCallback(r,t,3);o--&&r(n[o],o,n);)e++}else if(e=r,null==e||t)return n?n[u-1]:v;return p(n,Mt(0,u-e))}function jr(n,r,t){var e=n?n.length:0;for("number"==typeof t&&(e=(t<0?Mt(0,e+t):Vt(t,e-1))+1);e--;)if(n[e]===r)return e;return-1}function kr(n){for(var r=arguments,t=0,e=r.length,u=n?n.length:0;++t<e;)for(var o=-1,a=r[t];++o<u;)n[o]===a&&(Bt.call(n,o--,1),u--);return n}function xr(n,r,t){n=+n||0,t="number"==typeof t?t:+t||1,
+null==r&&(r=n,n=0);for(var e=-1,u=Mt(0,Et((r-n)/(t||1))),o=ht(u);++e<u;)o[e]=n,n+=t;return o}function Cr(n,r,t){var e=-1,u=n?n.length:0,o=[];for(r=h.createCallback(r,t,3);++e<u;){var a=n[e];r(a,e,n)&&(o.push(a),Bt.call(n,e--,1),u--)}return o}function Or(n,r,t){if("number"!=typeof r&&null!=r){var e=0,u=-1,o=n?n.length:0;for(r=h.createCallback(r,t,3);++u<o&&r(n[u],u,n);)e++}else e=null==r||t?1:Mt(0,r);return p(n,e)}function Nr(n,r,t,e){var u=0,o=n?n.length:u;for(t=t?h.createCallback(t,e,1):Yr,r=t(r);u<o;){
+var a=u+o>>>1;t(n[a])<r?u=a+1:o=a}return u}function Rr(){return un(nn(arguments,!0,!0))}function Er(n,r,t,e){return"boolean"!=typeof r&&null!=r&&(e=t,t="function"!=typeof r&&e&&e[r]===n?null:r,r=!1),null!=t&&(t=h.createCallback(t,e,3)),un(n,r,t)}function Ir(n){return Y(n,p(arguments,1))}function Sr(){for(var n=-1,r=arguments.length;++n<r;){var t=arguments[n];if(Yt(t)||vn(t))var e=e?un(Y(e,t).concat(Y(t,e))):t}return e||[]}function Ar(){for(var n=arguments.length>1?arguments:arguments[0],r=-1,t=n?rr(ve(n,"length")):0,e=ht(t<0?0:t);++r<t;)e[r]=ve(n,r);
+
+return e}function Dr(n,r){var t=-1,e=n?n.length:0,u={};for(r||!e||Yt(n[0])||(r=[]);++t<e;){var o=n[t];r?u[o]=r[t]:o&&(u[o[0]]=o[1])}return u}function Tr(n,r){if(!In(r))throw new kt;return function(){return--n<1?r.apply(this,arguments):v}}function $r(n,r){return arguments.length>2?an(n,17,p(arguments,2),null,r):an(n,1,null,null,r)}function Fr(n){for(var r=arguments.length>1?nn(arguments,!0,!1,1):wn(n),t=-1,e=r.length;++t<e;){var u=r[t];n[u]=an(n[u],1,null,null,n)}return n}function Br(n,r){return arguments.length>2?an(r,19,p(arguments,2),null,n):an(r,3,null,null,n);
+
+}function Wr(){for(var n=arguments,r=n.length;r--;)if(!In(n[r]))throw new kt;return function(){for(var r=arguments,t=n.length;t--;)r=[n[t].apply(this,r)];return r[0]}}function qr(n,r){return r="number"==typeof r?r:+r||n.length,an(n,4,null,null,null,r)}function zr(n,r,t){var e,u,o,a,i,f,l,c=0,p=!1,s=!0;if(!In(n))throw new kt;if(r=Mt(0,r)||0,t===!0){var h=!0;s=!1}else Sn(t)&&(h=t.leading,p="maxWait"in t&&(Mt(r,t.maxWait)||0),s="trailing"in t?t.trailing:s);var g=function(){var t=r-(ge()-a);if(t>0)f=Ft(g,t);
+else{u&&It(u);var p=l;u=f=l=v,p&&(c=ge(),o=n.apply(i,e),f||u||(e=i=null))}},y=function(){f&&It(f),u=f=l=v,(s||p!==r)&&(c=ge(),o=n.apply(i,e),f||u||(e=i=null))};return function(){if(e=arguments,a=ge(),i=this,l=s&&(f||!h),p===!1)var t=h&&!f;else{u||h||(c=a);var v=p-(a-c),m=v<=0;m?(u&&(u=It(u)),c=a,o=n.apply(i,e)):u||(u=Ft(y,v))}return m&&f?f=It(f):f||r===p||(f=Ft(g,r)),t&&(m=!0,o=n.apply(i,e)),!m||f||u||(e=i=null),o}}function Lr(n){if(!In(n))throw new kt;var r=p(arguments,1);return Ft(function(){n.apply(v,r);
+
+},1)}function Pr(n,r){if(!In(n))throw new kt;var t=p(arguments,2);return Ft(function(){n.apply(v,t)},r)}function Kr(n,r){if(!In(n))throw new kt;var t=function(){var e=t.cache,u=r?r.apply(this,arguments):m+arguments[0];return Tt.call(e,u)?e[u]:e[u]=n.apply(this,arguments)};return t.cache={},t}function Ur(n){var r,t;if(!In(n))throw new kt;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}}function Mr(n){return an(n,16,p(arguments,1))}function Vr(n){return an(n,32,null,p(arguments,1));
+
+}function Gr(n,r,t){var e=!0,u=!0;if(!In(n))throw new kt;return t===!1?e=!1:Sn(t)&&(e="leading"in t?t.leading:e,u="trailing"in t?t.trailing:u),U.leading=e,U.maxWait=r,U.trailing=u,zr(n,r,U)}function Hr(n,r){return an(r,16,[n])}function Jr(n){return function(){return n}}function Qr(n,r,t){var e=typeof n;if(null==n||"function"==e)return Q(n,r,t);if("object"!=e)return tt(n);var u=ne(n),o=u[0],a=n[o];return 1!=u.length||a!==a||Sn(a)?function(r){for(var t=u.length,e=!1;t--&&(e=rn(r[u[t]],n[u[t]],null,!0)););
+return e}:function(n){var r=n[o];return a===r&&(0!==a||1/a==1/r)}}function Xr(n){return null==n?"":jt(n).replace(ue,fn)}function Yr(n){return n}function Zr(n,r,t){var e=!0,u=r&&wn(r);r&&(t||u.length)||(null==t&&(t=r),o=g,r=n,n=h,u=wn(r)),t===!1?e=!1:Sn(t)&&"chain"in t&&(e=t.chain);var o=n,a=In(o);Xn(u,function(t){var u=n[t]=r[t];a&&(o.prototype[t]=function(){var r=this.__chain__,t=this.__wrapped__,a=[t];$t.apply(a,arguments);var i=u.apply(n,a);if(e||r){if(t===i&&Sn(i))return this;i=new o(i),i.__chain__=r;
+
+}return i})})}function nt(){return t._=Ot,this}function rt(){}function tt(n){return function(r){return r[n]}}function et(n,r,t){var e=null==n,u=null==r;if(null==t&&("boolean"==typeof n&&u?(t=n,n=1):u||"boolean"!=typeof r||(t=r,u=!0)),e&&u&&(r=1),n=+n||0,u?(r=n,n=0):r=+r||0,t||n%1||r%1){var o=Ht();return Vt(n+o*(r-n+parseFloat("1e-"+((o+"").length-1))),r)}return en(n,r)}function ut(n,r){if(n){var t=n[r];return In(t)?n[r]():t}}function ot(n,r,t){var e=h.templateSettings;n=jt(n||""),t=ae({},t,e);var u,o=ae({},t.imports,e.imports),i=ne(o),f=Un(o),l=0,c=t.interpolate||E,p="__p += '",s=wt((t.escape||E).source+"|"+c.source+"|"+(c===N?x:E).source+"|"+(t.evaluate||E).source+"|$","g");
+
+n.replace(s,function(r,t,e,o,i,f){return e||(e=o),p+=n.slice(l,f).replace(S,a),t&&(p+="' +\n__e("+t+") +\n'"),i&&(u=!0,p+="';\n"+i+";\n__p += '"),e&&(p+="' +\n((__t = ("+e+")) == null ? '' : __t) +\n'"),l=f+r.length,r}),p+="';\n";var g=t.variable,y=g;y||(g="obj",p="with ("+g+") {\n"+p+"\n}\n"),p=(u?p.replace(w,""):p).replace(j,"$1").replace(k,"$1;"),p="function("+g+") {\n"+(y?"":g+" || ("+g+" = {});\n")+"var __t, __p = '', __e = _.escape"+(u?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+p+"return __p\n}";
+
+var m="\n/*\n//# sourceURL="+(t.sourceURL||"/lodash/template/source["+D++ +"]")+"\n*/";try{var b=mt(i,"return "+p+m).apply(v,f)}catch(_){throw _.source=p,_}return r?b(r):(b.source=p,b)}function at(n,r,t){n=(n=+n)>-1?n:0;var e=-1,u=ht(n);for(r=Q(r,t,1);++e<n;)u[e]=r(e);return u}function it(n){return null==n?"":jt(n).replace(ee,sn)}function ft(n){var r=++y;return jt(null==n?"":n)+r}function lt(n){return n=new g(n),n.__chain__=!0,n}function ct(n,r){return r(n),n}function pt(){return this.__chain__=!0,
+this}function st(){return jt(this.__wrapped__)}function vt(){return this.__wrapped__}t=t?Z.defaults(H.Object(),t,Z.pick(H,A)):H;var ht=t.Array,gt=t.Boolean,yt=t.Date,mt=t.Function,bt=t.Math,_t=t.Number,dt=t.Object,wt=t.RegExp,jt=t.String,kt=t.TypeError,xt=[],Ct=dt.prototype,Ot=t._,Nt=Ct.toString,Rt=wt("^"+jt(Nt).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),Et=bt.ceil,It=t.clearTimeout,St=bt.floor,At=mt.prototype.toString,Dt=cn(Dt=dt.getPrototypeOf)&&Dt,Tt=Ct.hasOwnProperty,$t=xt.push,Ft=t.setTimeout,Bt=xt.splice,Wt=xt.unshift,qt=function(){
+try{var n={},r=cn(r=dt.defineProperty)&&r,t=r(n,n,n)&&r}catch(e){}return t}(),zt=cn(zt=dt.create)&&zt,Lt=cn(Lt=ht.isArray)&&Lt,Pt=t.isFinite,Kt=t.isNaN,Ut=cn(Ut=dt.keys)&&Ut,Mt=bt.max,Vt=bt.min,Gt=t.parseInt,Ht=bt.random,Jt={};Jt[$]=ht,Jt[F]=gt,Jt[B]=yt,Jt[W]=mt,Jt[z]=dt,Jt[q]=_t,Jt[L]=wt,Jt[P]=jt,g.prototype=h.prototype;var Qt=h.support={};Qt.funcDecomp=!cn(t.WinRTError)&&I.test(s),Qt.funcNames="string"==typeof mt.name,h.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:N,
+variable:"",imports:{_:h}},zt||(J=function(){function n(){}return function(r){if(Sn(r)){n.prototype=r;var e=new n;n.prototype=null}return e||t.Object()}}());var Xt=qt?function(n,r){M.value=r,qt(n,"__bindData__",M),M.value=null}:rt,Yt=Lt||function(n){return n&&"object"==typeof n&&"number"==typeof n.length&&Nt.call(n)==$||!1},Zt=function(n){var r,t=n,e=[];if(!t)return e;if(!V[typeof n])return e;for(r in t)Tt.call(t,r)&&e.push(r);return e},ne=Ut?function(n){return Sn(n)?Ut(n):[]}:Zt,re={"&":"&",
+"<":"<",">":">",'"':""","'":"'"},te=kn(re),ee=wt("("+ne(te).join("|")+")","g"),ue=wt("["+ne(re).join("")+"]","g"),oe=function(n,r,t){var e,u=n,o=u;if(!u)return o;var a=arguments,i=0,f="number"==typeof t?2:a.length;if(f>3&&"function"==typeof a[f-2])var l=Q(a[--f-1],a[f--],2);else f>2&&"function"==typeof a[f-1]&&(l=a[--f]);for(;++i<f;)if(u=a[i],u&&V[typeof u])for(var c=-1,p=V[typeof u]&&ne(u),s=p?p.length:0;++c<s;)e=p[c],o[e]=l?l(o[e],u[e]):u[e];return o},ae=function(n,r,t){var e,u=n,o=u;
+
+if(!u)return o;for(var a=arguments,i=0,f="number"==typeof t?2:a.length;++i<f;)if(u=a[i],u&&V[typeof u])for(var l=-1,c=V[typeof u]&&ne(u),p=c?c.length:0;++l<p;)e=c[l],"undefined"==typeof o[e]&&(o[e]=u[e]);return o},ie=function(n,r,t){var e,u=n,o=u;if(!u)return o;if(!V[typeof u])return o;r=r&&"undefined"==typeof t?r:Q(r,t,3);for(e in u)if(r(u[e],e,n)===!1)return o;return o},fe=function(n,r,t){var e,u=n,o=u;if(!u)return o;if(!V[typeof u])return o;r=r&&"undefined"==typeof t?r:Q(r,t,3);for(var a=-1,i=V[typeof u]&&ne(u),f=i?i.length:0;++a<f;)if(e=i[a],
+r(u[e],e,n)===!1)return o;return o},le=Dt?function(n){if(!n||Nt.call(n)!=z)return!1;var r=n.valueOf,t=cn(r)&&(t=Dt(r))&&Dt(t);return t?n==t||Dt(n)==t:pn(n)}:pn,ce=on(function(n,r,t){Tt.call(n,t)?n[t]++:n[t]=1}),pe=on(function(n,r,t){(Tt.call(n,t)?n[t]:n[t]=[]).push(r)}),se=on(function(n,r,t){n[t]=r}),ve=nr,he=Hn,ge=cn(ge=yt.now)&&ge||function(){return(new yt).getTime()},ye=8==Gt(d+"08")?Gt:function(n,r){return Gt(Fn(n)?n.replace(R,""):n,r||0)};return h.after=Tr,h.assign=oe,h.at=Mn,h.bind=$r,h.bindAll=Fr,
+h.bindKey=Br,h.chain=lt,h.compact=sr,h.compose=Wr,h.constant=Jr,h.countBy=ce,h.create=yn,h.createCallback=Qr,h.curry=qr,h.debounce=zr,h.defaults=ae,h.defer=Lr,h.delay=Pr,h.difference=vr,h.filter=Hn,h.flatten=mr,h.forEach=Xn,h.forEachRight=Yn,h.forIn=ie,h.forInRight=_n,h.forOwn=fe,h.forOwnRight=dn,h.functions=wn,h.groupBy=pe,h.indexBy=se,h.initial=_r,h.intersection=dr,h.invert=kn,h.invoke=Zn,h.keys=ne,h.map=nr,h.mapValues=Wn,h.max=rr,h.memoize=Kr,h.merge=qn,h.min=tr,h.omit=zn,h.once=Ur,h.pairs=Ln,
+h.partial=Mr,h.partialRight=Vr,h.pick=Pn,h.pluck=ve,h.property=tt,h.pull=kr,h.range=xr,h.reject=or,h.remove=Cr,h.rest=Or,h.shuffle=ir,h.sortBy=cr,h.tap=ct,h.throttle=Gr,h.times=at,h.toArray=pr,h.transform=Kn,h.union=Rr,h.uniq=Er,h.values=Un,h.where=he,h.without=Ir,h.wrap=Hr,h.xor=Sr,h.zip=Ar,h.zipObject=Dr,h.collect=nr,h.drop=Or,h.each=Xn,h.eachRight=Yn,h.extend=oe,h.methods=wn,h.object=Dr,h.select=Hn,h.tail=Or,h.unique=Er,h.unzip=Ar,Zr(h),h.clone=hn,h.cloneDeep=gn,h.contains=Vn,h.escape=Xr,h.every=Gn,
+h.find=Jn,h.findIndex=hr,h.findKey=mn,h.findLast=Qn,h.findLastIndex=gr,h.findLastKey=bn,h.has=jn,h.identity=Yr,h.indexOf=br,h.isArguments=vn,h.isArray=Yt,h.isBoolean=xn,h.isDate=Cn,h.isElement=On,h.isEmpty=Nn,h.isEqual=Rn,h.isFinite=En,h.isFunction=In,h.isNaN=An,h.isNull=Dn,h.isNumber=Tn,h.isObject=Sn,h.isPlainObject=le,h.isRegExp=$n,h.isString=Fn,h.isUndefined=Bn,h.lastIndexOf=jr,h.mixin=Zr,h.noConflict=nt,h.noop=rt,h.now=ge,h.parseInt=ye,h.random=et,h.reduce=er,h.reduceRight=ur,h.result=ut,h.runInContext=s,
+h.size=fr,h.some=lr,h.sortedIndex=Nr,h.template=ot,h.unescape=it,h.uniqueId=ft,h.all=Gn,h.any=lr,h.detect=Jn,h.findWhere=Jn,h.foldl=er,h.foldr=ur,h.include=Vn,h.inject=er,Zr(function(){var n={};return fe(h,function(r,t){h.prototype[t]||(n[t]=r)}),n}(),!1),h.first=yr,h.last=wr,h.sample=ar,h.take=yr,h.head=yr,fe(h,function(n,r){var t="sample"!==r;h.prototype[r]||(h.prototype[r]=function(r,e){var u=this.__chain__,o=n(this.__wrapped__,r,e);return u||null!=r&&(!e||t&&"function"==typeof r)?new g(o,u):o;
+
+})}),h.VERSION="2.4.2",h.prototype.chain=pt,h.prototype.toString=st,h.prototype.value=vt,h.prototype.valueOf=vt,Xn(["join","pop","shift"],function(n){var r=xt[n];h.prototype[n]=function(){var n=this.__chain__,t=r.apply(this.__wrapped__,arguments);return n?new g(t,n):t}}),Xn(["push","reverse","sort","unshift"],function(n){var r=xt[n];h.prototype[n]=function(){return r.apply(this.__wrapped__,arguments),this}}),Xn(["concat","slice","splice"],function(n){var r=xt[n];h.prototype[n]=function(){return new g(r.apply(this.__wrapped__,arguments),this.__chain__);
+
+}}),h}var v,h=[],g=[],y=0,m=+new Date+"",b=75,_=40,d=" \v\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",w=/\b__p \+= '';/g,j=/\b(__p \+=) '' \+/g,k=/(__e\(.*?\)|\b__t\)) \+\n'';/g,x=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,C=/\w*$/,O=/^\s*function[ \n\r\t]+\w/,N=/<%=([\s\S]+?)%>/g,R=RegExp("^["+d+"]*0+(?=.$)"),E=/($^)/,I=/\bthis\b/,S=/['\n\r\t\u2028\u2029\\]/g,A=["Array","Boolean","Date","Function","Math","Number","Object","RegExp","String","_","attachEvent","clearTimeout","isFinite","isNaN","parseInt","setTimeout"],D=0,T="[object Arguments]",$="[object Array]",F="[object Boolean]",B="[object Date]",W="[object Function]",q="[object Number]",z="[object Object]",L="[object RegExp]",P="[object String]",K={};
+
+K[W]=!1,K[T]=K[$]=K[F]=K[B]=K[q]=K[z]=K[L]=K[P]=!0;var U={leading:!1,maxWait:0,trailing:!1},M={configurable:!1,enumerable:!1,value:null,writable:!1},V={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,undefined:!1},G={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"},H=V[typeof window]&&window||this,J=V[typeof exports]&&exports&&!exports.nodeType&&exports,Q=V[typeof module]&&module&&!module.nodeType&&module,X=Q&&Q.exports===J&&J,Y=V[typeof global]&&global;!Y||Y.global!==Y&&Y.window!==Y||(H=Y);
+
+var Z=s();"function"==typeof define&&"object"==typeof define.amd&&define.amd?(H._=Z,define(function(){return Z})):J&&Q?X?(Q.exports=Z)._=Z:J._=Z:H._=Z}).call(this);
\ No newline at end of file
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 (Custom Build) <https://lodash.com/>
+ * Build: `lodash underscore exports="amd,commonjs,global,node" -o ./dist/lodash.underscore.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <https://lodash.com/license>
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre ES5 environments */
+ var undefined;
+
+ /** Used to generate unique IDs */
+ var idCounter = 0;
+
+ /** Used internally to indicate various things */
+ var indicatorObject = {};
+
+ /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+ var keyPrefix = +new Date + '';
+
+ /** Used to match "interpolate" template delimiters */
+ var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+ /** Used to ensure capturing order of template delimiters */
+ var reNoMatch = /($^)/;
+
+ /** Used to match unescaped characters in compiled string literals */
+ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+ /** `Object#toString` result shortcuts */
+ var argsClass = '[object Arguments]',
+ arrayClass = '[object Array]',
+ boolClass = '[object Boolean]',
+ dateClass = '[object Date]',
+ funcClass = '[object Function]',
+ numberClass = '[object Number]',
+ objectClass = '[object Object]',
+ regexpClass = '[object RegExp]',
+ stringClass = '[object String]';
+
+ /** Used to determine if values are of the language type Object */
+ var objectTypes = {
+ 'boolean': false,
+ 'function': true,
+ 'object': true,
+ 'number': false,
+ 'string': false,
+ 'undefined': false
+ };
+
+ /** Used to escape characters for inclusion in compiled string literals */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ /** Used as a reference to the global object */
+ var root = (objectTypes[typeof window] && window) || this;
+
+ /** Detect free variable `exports` */
+ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+ /** Detect free variable `module` */
+ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+ /** Detect the popular CommonJS extension `module.exports` */
+ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+ /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+ var freeGlobal = objectTypes[typeof global] && global;
+ if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+ root = freeGlobal;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.indexOf` without support for binary searches
+ * or `fromIndex` constraints.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+ * them in ascending order.
+ *
+ * @private
+ * @param {Object} a The object to compare to `b`.
+ * @param {Object} b The object to compare to `a`.
+ * @returns {number} Returns the sort order indicator of `1` or `-1`.
+ */
+ function compareAscending(a, b) {
+ var ac = a.criteria,
+ bc = b.criteria,
+ index = -1,
+ length = ac.length;
+
+ while (++index < length) {
+ var value = ac[index],
+ other = bc[index];
+
+ if (value !== other) {
+ if (value > other || typeof value == 'undefined') {
+ return 1;
+ }
+ if (value < other || typeof other == 'undefined') {
+ return -1;
+ }
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to return the same value for
+ // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See http://code.google.com/p/v8/issues/detail?id=90
+ return a.index - b.index;
+ }
+
+ /**
+ * Used by `template` to escape characters for inclusion in compiled
+ * string literals.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(match) {
+ return '\\' + stringEscapes[match];
+ }
+
+ /**
+ * Slices the `collection` from the `start` index up to, but not including,
+ * the `end` index.
+ *
+ * Note: This function is used instead of `Array#slice` to support node lists
+ * in IE < 9 and to ensure dense arrays are returned.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to slice.
+ * @param {number} start The start index.
+ * @param {number} end The end index.
+ * @returns {Array} Returns the new array.
+ */
+ function slice(array, start, end) {
+ start || (start = 0);
+ if (typeof end == 'undefined') {
+ end = array ? array.length : 0;
+ }
+ var index = -1,
+ length = end - start || 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = array[start + index];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Used for `Array` method references.
+ *
+ * Normally `Array.prototype` would suffice, however, using an array literal
+ * avoids issues in Narwhal.
+ */
+ var arrayRef = [];
+
+ /** Used for native method references */
+ var objectProto = Object.prototype;
+
+ /** Used to restore the original `_` reference in `noConflict` */
+ var oldDash = root._;
+
+ /** Used to resolve the internal [[Class]] of values */
+ var toString = objectProto.toString;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(toString)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/toString| for [^\]]+/g, '.*?') + '$'
+ );
+
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ floor = Math.floor,
+ hasOwnProperty = objectProto.hasOwnProperty,
+ push = arrayRef.push,
+ propertyIsEnumerable = objectProto.propertyIsEnumerable;
+
+ /* Native method shortcuts for methods with the same name as other `lodash` methods */
+ var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+ nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+ nativeIsFinite = root.isFinite,
+ nativeIsNaN = root.isNaN,
+ nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeRandom = Math.random;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps the given value to enable intuitive
+ * method chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+ * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+ * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+ * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+ * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+ * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * and `zip`
+ *
+ * The non-chainable wrapper functions are:
+ * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+ * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+ * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+ * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+ * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+ * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+ * `template`, `unescape`, `uniqueId`, and `value`
+ *
+ * The wrapper functions `first` and `last` return wrapped values when `n` is
+ * provided, otherwise they return unwrapped values.
+ *
+ * Explicit chaining can be enabled by using the `_.chain` method.
+ *
+ * @name _
+ * @constructor
+ * @category Chaining
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(num) {
+ * return num * num;
+ * });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ return (value instanceof lodash)
+ ? value
+ : new lodashWrapper(value);
+ }
+
+ /**
+ * A fast path for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @param {boolean} chainAll A flag to enable chaining for all methods
+ * @returns {Object} Returns a `lodash` instance.
+ */
+ function lodashWrapper(value, chainAll) {
+ this.__chain__ = !!chainAll;
+ this.__wrapped__ = value;
+ }
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
+
+ /**
+ * An object used to flag environments features.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ var support = {};
+
+ (function() {
+ var object = { '0': 1, 'length': 1 };
+
+ /**
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
+ *
+ * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
+ * and `splice()` functions that fail to remove the last element, `value[0]`,
+ * of array-like objects even though the `length` property is set to `0`.
+ * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
+ * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
+ }(1));
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in
+ * embedded Ruby (ERB). Change the following template settings to use alternative
+ * delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': /<%-([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': /<%([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
+
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type string
+ */
+ 'variable': ''
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.bind` that creates the bound function and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new bound function.
+ */
+ function baseBind(bindData) {
+ var func = bindData[0],
+ partialArgs = bindData[2],
+ thisArg = bindData[4];
+
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.io/#x15.3.4.5
+ if (partialArgs) {
+ // avoid `arguments` object deoptimizations by using `slice` instead
+ // of `Array.prototype.slice.call` and not assigning `arguments` to a
+ // variable as a ternary expression
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ // mimic the constructor's `return` behavior
+ // http://es5.github.io/#x13.2.2
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ var thisBinding = baseCreate(func.prototype),
+ result = func.apply(thisBinding, args || arguments);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisArg, args || arguments);
+ }
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ function baseCreate(prototype, properties) {
+ return isObject(prototype) ? nativeCreate(prototype) : {};
+ }
+ // fallback for browsers without `Object.create`
+ if (!nativeCreate) {
+ baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || root.Object();
+ };
+ }());
+ }
+
+ /**
+ * The base implementation of `_.createCallback` without support for creating
+ * "_.pluck" or "_.where" style callbacks.
+ *
+ * @private
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ */
+ function baseCreateCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ // exit early for no `thisArg` or already bound by `Function#bind`
+ if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 2: return function(a, b) {
+ return func.call(thisArg, a, b);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ }
+ return bind(func, thisArg);
+ }
+
+ /**
+ * The base implementation of `createWrapper` that creates the wrapper and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new function.
+ */
+ function baseCreateWrapper(bindData) {
+ var func = bindData[0],
+ bitmask = bindData[1],
+ partialArgs = bindData[2],
+ partialRightArgs = bindData[3],
+ thisArg = bindData[4],
+ arity = bindData[5];
+
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ key = func;
+
+ function bound() {
+ var thisBinding = isBind ? thisArg : this;
+ if (partialArgs) {
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ if (partialRightArgs || isCurry) {
+ args || (args = slice(arguments));
+ if (partialRightArgs) {
+ push.apply(args, partialRightArgs);
+ }
+ if (isCurry && args.length < arity) {
+ bitmask |= 16 & ~32;
+ return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+ }
+ }
+ args || (args = arguments);
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (this instanceof bound) {
+ thisBinding = baseCreate(func.prototype);
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.difference` that accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {Array} [values] The array of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` without support for callback
+ * shorthands or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+ * @param {number} [fromIndex=0] The index to start from.
+ * @returns {Array} Returns a new flattened array.
+ */
+ function baseFlatten(array, isShallow, isStrict, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+
+ if (value && typeof value == 'object' && typeof value.length == 'number'
+ && (isArray(value) || isArguments(value))) {
+ // recursively flatten arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ value = baseFlatten(value, isShallow, isStrict);
+ }
+ var valIndex = -1,
+ valLength = value.length,
+ resIndex = result.length;
+
+ result.length += valLength;
+ while (++valIndex < valLength) {
+ result[resIndex++] = value[valIndex];
+ }
+ } else if (!isStrict) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+ * that allows partial "_.where" style comparisons.
+ *
+ * @private
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(a, b, stackA, stackB) {
+ if (a === b) {
+ return a !== 0 || (1 / a == 1 / b);
+ }
+ var type = typeof a,
+ otherType = typeof b;
+
+ if (a === a &&
+ !(a && objectTypes[type]) &&
+ !(b && objectTypes[otherType])) {
+ return false;
+ }
+ if (a == null || b == null) {
+ return a === b;
+ }
+ var className = toString.call(a),
+ otherClass = toString.call(b);
+
+ if (className != otherClass) {
+ return false;
+ }
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return +a == +b;
+
+ case numberClass:
+ return a != +a
+ ? b != +b
+ : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+ case regexpClass:
+ case stringClass:
+ return a == String(b);
+ }
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ var aWrapped = a instanceof lodash,
+ bWrapped = b instanceof lodash;
+
+ if (aWrapped || bWrapped) {
+ return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, stackA, stackB);
+ }
+ if (className != objectClass) {
+ return false;
+ }
+ var ctorA = a.constructor,
+ ctorB = b.constructor;
+
+ if (ctorA != ctorB &&
+ !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+ ('constructor' in a && 'constructor' in b)
+ ) {
+ return false;
+ }
+ }
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
+ }
+ }
+ var result = true,
+ size = 0;
+
+ stackA.push(a);
+ stackB.push(b);
+
+ if (isArr) {
+ size = b.length;
+ result = size == a.length;
+
+ if (result) {
+ while (size--) {
+ if (!(result = baseIsEqual(a[size], b[size], stackA, stackB))) {
+ break;
+ }
+ }
+ }
+ }
+ else {
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ size++;
+ return !(result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, stackA, stackB)) && indicatorObject;
+ }
+ });
+
+ if (result) {
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ return !(result = --size > -1) && indicatorObject;
+ }
+ });
+ }
+ }
+ stackA.pop();
+ stackB.pop();
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * The base implementation of `_.uniq` without support for callback shorthands
+ * or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function} [callback] The function called per iteration.
+ * @returns {Array} Returns a duplicate-value-free array.
+ */
+ function baseUniq(array, isSorted, callback) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [],
+ seen = callback ? [] : result;
+
+ while (++index < length) {
+ var value = array[index],
+ computed = callback ? callback(value, index, array) : value;
+
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : indexOf(seen, computed) < 0
+ ) {
+ if (callback) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates a function that aggregates a collection, creating an object composed
+ * of keys generated from the results of running each element of the collection
+ * through a callback. The given `setter` function sets the keys and values
+ * of the composed object.
+ *
+ * @private
+ * @param {Function} setter The setter function.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter) {
+ return function(collection, callback, thisArg) {
+ var result = {};
+ callback = createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ setter(result, value, callback(value, index, collection), collection);
+ }
+ } else {
+ forOwn(collection, function(value, key, collection) {
+ setter(result, value, callback(value, key, collection), collection);
+ });
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, either curries or invokes `func`
+ * with an optional `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of method flags to compose.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry`
+ * 8 - `_.curry` (bound)
+ * 16 - `_.partial`
+ * 32 - `_.partialRight`
+ * @param {Array} [partialArgs] An array of arguments to prepend to those
+ * provided to the new function.
+ * @param {Array} [partialRightArgs] An array of arguments to append to those
+ * provided to the new function.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new function.
+ */
+ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ isPartial = bitmask & 16,
+ isPartialRight = bitmask & 32;
+
+ if (!isBindKey && !isFunction(func)) {
+ throw new TypeError;
+ }
+ if (isPartial && !partialArgs.length) {
+ bitmask &= ~16;
+ isPartial = partialArgs = false;
+ }
+ if (isPartialRight && !partialRightArgs.length) {
+ bitmask &= ~32;
+ isPartialRight = partialRightArgs = false;
+ }
+ // fast path for `_.bind`
+ var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+ return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+ }
+
+ /**
+ * Used by `escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(match) {
+ return htmlEscapes[match];
+ }
+
+ /**
+ * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+ * customized, this method returns the custom method, otherwise it returns
+ * the `baseIndexOf` function.
+ *
+ * @private
+ * @returns {Function} Returns the "indexOf" function.
+ */
+ function getIndexOf() {
+ var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a native function.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+ */
+ function isNative(value) {
+ return typeof value == 'function' && reNative.test(value);
+ }
+
+ /**
+ * Used by `unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} match The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ function unescapeHtmlChar(match) {
+ return htmlUnescapes[match];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if `value` is an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })(1, 2, 3);
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == argsClass || false;
+ }
+ // fallback for browsers that can't detect `arguments` objects by [[Class]]
+ if (!isArguments(arguments)) {
+ isArguments = function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false;
+ };
+ }
+
+ /**
+ * Checks if `value` is an array.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+ * @example
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ */
+ var isArray = nativeIsArray || function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == arrayClass || false;
+ };
+
+ /**
+ * A fallback implementation of `Object.keys` which produces an array of the
+ * given object's own enumerable property names.
+ *
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ */
+ var shimKeys = function(object) {
+ var index, iterable = object, result = [];
+ if (!iterable) return result;
+ if (!(objectTypes[typeof object])) return result;
+ for (index in iterable) {
+ if (hasOwnProperty.call(iterable, index)) {
+ result.push(index);
+ }
+ }
+ return result
+ };
+
+ /**
+ * Creates an array composed of the own enumerable property names of an object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ * @example
+ *
+ * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+ */
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (!isObject(object)) {
+ return [];
+ }
+ return nativeKeys(object);
+ };
+
+ /**
+ * Used to convert characters to HTML entities:
+ *
+ * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+ * don't require escaping in HTML and have no special meaning unless they're part
+ * of a tag or an unquoted attribute value.
+ * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+ */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
+
+ /** Used to match HTML entities and HTML characters */
+ var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+ reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property assignments of previous
+ * sources. If a callback is provided it will be executed to produce the
+ * assigned values. The callback is bound to `thisArg` and invoked with two
+ * arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @alias extend
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize assigning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+ * // => { 'name': 'fred', 'employer': 'slate' }
+ *
+ * var defaults = _.partialRight(_.assign, function(a, b) {
+ * return typeof a == 'undefined' ? b : a;
+ * });
+ *
+ * var object = { 'name': 'barney' };
+ * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ function assign(object) {
+ if (!object) {
+ return object;
+ }
+ for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
+ var iterable = arguments[argsIndex];
+ if (iterable) {
+ for (var key in iterable) {
+ object[key] = iterable[key];
+ }
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+ * be cloned, otherwise they will be assigned by reference. If a callback
+ * is provided it will be executed to produce the cloned values. If the
+ * callback returns `undefined` cloning will be handled by the method instead.
+ * The callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var shallow = _.clone(characters);
+ * shallow[0] === characters[0];
+ * // => true
+ *
+ * var deep = _.clone(characters, true);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * _.mixin({
+ * 'clone': _.partialRight(_.clone, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * })
+ * });
+ *
+ * var clone = _.clone(document.body);
+ * clone.childNodes.length;
+ * // => 0
+ */
+ function clone(value) {
+ return isObject(value)
+ ? (isArray(value) ? slice(value) : assign({}, value))
+ : value;
+ }
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property will be ignored.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param- {Object} [guard] Allows working with `_.reduce` without using its
+ * `key` and `object` arguments as sources.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var object = { 'name': 'barney' };
+ * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ function defaults(object) {
+ if (!object) {
+ return object;
+ }
+ for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
+ var iterable = arguments[argsIndex];
+ if (iterable) {
+ for (var key in iterable) {
+ if (typeof object[key] == 'undefined') {
+ object[key] = iterable[key];
+ }
+ }
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Iterates over own and inherited enumerable properties of an object,
+ * executing the callback for each property. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, key, object). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forIn(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+ */
+ var forIn = function(collection, callback) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+ for (index in iterable) {
+ if (callback(iterable[index], index, collection) === indicatorObject) return result;
+ }
+ return result
+ };
+
+ /**
+ * Iterates over own enumerable properties of an object, executing the callback
+ * for each property. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, key, object). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+ */
+ var forOwn = function(collection, callback) {
+ var index, iterable = collection, result = iterable;
+ if (!iterable) return result;
+ if (!objectTypes[typeof iterable]) return result;
+ for (index in iterable) {
+ if (hasOwnProperty.call(iterable, index)) {
+ if (callback(iterable[index], index, collection) === indicatorObject) return result;
+ }
+ }
+ return result
+ };
+
+ /**
+ * Creates a sorted array of property names of all enumerable properties,
+ * own and inherited, of `object` that have function values.
+ *
+ * @static
+ * @memberOf _
+ * @alias methods
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names that have function values.
+ * @example
+ *
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+ */
+ function functions(object) {
+ var result = [];
+ forIn(object, function(value, key) {
+ if (isFunction(value)) {
+ result.push(key);
+ }
+ });
+ return result.sort();
+ }
+
+ /**
+ * Checks if the specified property name exists as a direct property of `object`,
+ * instead of an inherited property.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to check.
+ * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+ * @example
+ *
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
+ */
+ function has(object, key) {
+ return object ? hasOwnProperty.call(object, key) : false;
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of the given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the created inverted object.
+ * @example
+ *
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
+ */
+ function invert(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ result[object[key]] = key;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a boolean value.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+ * @example
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass || false;
+ }
+
+ /**
+ * Checks if `value` is a date.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ */
+ function isDate(value) {
+ return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+ }
+
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ */
+ function isElement(value) {
+ return value && value.nodeType === 1 || false;
+ }
+
+ /**
+ * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+ * length of `0` and objects with no own enumerable properties are considered
+ * "empty".
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object|string} value The value to inspect.
+ * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({});
+ * // => true
+ *
+ * _.isEmpty('');
+ * // => true
+ */
+ function isEmpty(value) {
+ if (!value) {
+ return true;
+ }
+ if (isArray(value) || isString(value)) {
+ return !value.length;
+ }
+ for (var key in value) {
+ if (hasOwnProperty.call(value, key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent to each other. If a callback is provided it will be executed
+ * to compare values. If the callback returns `undefined` comparisons will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (a, b).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var copy = { 'name': 'fred' };
+ *
+ * object == copy;
+ * // => false
+ *
+ * _.isEqual(object, copy);
+ * // => true
+ *
+ * var words = ['hello', 'goodbye'];
+ * var otherWords = ['hi', 'goodbye'];
+ *
+ * _.isEqual(words, otherWords, function(a, b) {
+ * var reGreet = /^(?:hello|hi)$/i,
+ * aGreet = _.isString(a) && reGreet.test(a),
+ * bGreet = _.isString(b) && reGreet.test(b);
+ *
+ * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * });
+ * // => true
+ */
+ function isEqual(a, b) {
+ return baseIsEqual(a, b);
+ }
+
+ /**
+ * Checks if `value` is, or can be coerced to, a finite number.
+ *
+ * Note: This is not the same as native `isFinite` which will return true for
+ * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+ }
+
+ /**
+ * Checks if `value` is a function.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ */
+ function isFunction(value) {
+ return typeof value == 'function';
+ }
+ // fallback for older versions of Chrome and Safari
+ if (isFunction(/x/)) {
+ isFunction = function(value) {
+ return typeof value == 'function' && toString.call(value) == funcClass;
+ };
+ }
+
+ /**
+ * Checks if `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.io/#x8
+ // and avoid a V8 bug
+ // http://code.google.com/p/v8/issues/detail?id=2291
+ return !!(value && objectTypes[typeof value]);
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * Note: This is not the same as native `isNaN` which will return `true` for
+ * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // `NaN` as a primitive is the only value that is not equal to itself
+ // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(undefined);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is a number.
+ *
+ * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4 * 5);
+ * // => true
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ value && typeof value == 'object' && toString.call(value) == numberClass || false;
+ }
+
+ /**
+ * Checks if `value` is a regular expression.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+ * @example
+ *
+ * _.isRegExp(/fred/);
+ * // => true
+ */
+ function isRegExp(value) {
+ return value && objectTypes[typeof value] && toString.call(value) == regexpClass || false;
+ }
+
+ /**
+ * Checks if `value` is a string.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('fred');
+ * // => true
+ */
+ function isString(value) {
+ return typeof value == 'string' ||
+ value && typeof value == 'object' && toString.call(value) == stringClass || false;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Creates a shallow clone of `object` excluding the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` omitting the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The properties to omit or the
+ * function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object without the omitted properties.
+ * @example
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+ * // => { 'name': 'fred' }
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function omit(object) {
+ var props = [];
+ forIn(object, function(value, key) {
+ props.push(key);
+ });
+ props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+ var index = -1,
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ result[key] = object[key];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a two dimensional array of an object's key-value pairs,
+ * i.e. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a shallow clone of `object` composed of the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` picking the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The function called per
+ * iteration or property names to pick, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
+ * @example
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+ * // => { 'name': 'fred' }
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function pick(object) {
+ var index = -1,
+ props = baseFlatten(arguments, true, false, 1),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array composed of the own enumerable property values of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property values.
+ * @example
+ *
+ * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => [1, 2, 3] (property order is not guaranteed across environments)
+ */
+ function values(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if a given value is present in a collection using strict equality
+ * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+ * offset from the end of the collection.
+ *
+ * @static
+ * @memberOf _
+ * @alias include
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {*} target The value to check for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+ * @example
+ *
+ * _.contains([1, 2, 3], 1);
+ * // => true
+ *
+ * _.contains([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+ * // => true
+ *
+ * _.contains('pebbles', 'eb');
+ * // => true
+ */
+ function contains(collection, target) {
+ var indexOf = getIndexOf(),
+ length = collection ? collection.length : 0,
+ result = false;
+ if (length && typeof length == 'number') {
+ result = indexOf(collection, target) > -1;
+ } else {
+ forOwn(collection, function(value) {
+ return (result = value === target) && indicatorObject;
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through the callback. The corresponding value
+ * of each key is the number of times the key was returned by the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+ });
+
+ /**
+ * Checks if the given callback returns truey value for **all** elements of
+ * a collection. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if all elements passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes']);
+ * // => false
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(characters, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(characters, { 'age': 36 });
+ * // => false
+ */
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return !(result = !!callback(value, index, collection)) && indicatorObject;
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning an array of all elements
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that passed the callback check.
+ * @example
+ *
+ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [2, 4, 6]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.filter(characters, 'blocked');
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ */
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning the first element that
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect, findWhere
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.find(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+ *
+ * // using "_.where" callback shorthand
+ * _.find(characters, { 'age': 1 });
+ * // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(characters, 'blocked');
+ * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+ */
+ function find(collection, callback, thisArg) {
+ callback = createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ return value;
+ }
+ }
+ } else {
+ var result;
+ forOwn(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return indicatorObject;
+ }
+ });
+ return result;
+ }
+ }
+
+ /**
+ * Examines each element in a `collection`, returning the first that
+ * has the given properties. When checking `properties`, this method
+ * performs a deep comparison between values to determine if they are
+ * equivalent to each other.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Object} properties The object of property values to filter by.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'banana', 'organic': true, 'type': 'fruit' },
+ * { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ * ];
+ *
+ * _.findWhere(food, { 'type': 'vegetable' });
+ * // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ */
+ function findWhere(object, properties) {
+ return where(object, properties, true);
+ }
+
+ /**
+ * Iterates over elements of a collection, executing the callback for each
+ * element. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * Note: As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+ * // => logs each number and returns '1,2,3'
+ *
+ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+ * // => logs each number and returns the object (property order is not guaranteed across environments)
+ */
+ function forEach(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if (callback(collection[index], index, collection) === indicatorObject) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, callback);
+ }
+ }
+
+ /**
+ * This method is like `_.forEach` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias eachRight
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+ * // => logs each number from right to left and returns '3,2,1'
+ */
+ function forEachRight(collection, callback) {
+ var length = collection ? collection.length : 0;
+ if (typeof length == 'number') {
+ while (length--) {
+ if (callback(collection[length], length, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ var props = keys(collection);
+ length = props.length;
+ forOwn(collection, function(value, key, collection) {
+ key = props ? props[--length] : --length;
+ return callback(collection[key], key, collection) === false && indicatorObject;
+ });
+ }
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of a collection through the callback. The corresponding value
+ * of each key is an array of the elements responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+ });
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of the collection through the given callback. The corresponding
+ * value of each key is the last element responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var keys = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.indexBy(keys, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ */
+ var indexBy = createAggregator(function(result, value, key) {
+ result[key] = value;
+ });
+
+ /**
+ * Invokes the method named by `methodName` on each element in the `collection`
+ * returning an array of the results of each invoked method. Additional arguments
+ * will be provided to each invoked method. If `methodName` is a function it
+ * will be invoked for, and `this` bound to, each element in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [arg] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of the results of each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ var args = slice(arguments, 2),
+ index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+ });
+ return result;
+ }
+
+ /**
+ * Creates an array of values by running each element in the collection
+ * through the callback. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @example
+ *
+ * _.map([1, 2, 3], function(num) { return num * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+ * // => [3, 6, 9] (property order is not guaranteed across environments)
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = createCallback(callback, thisArg, 3);
+ if (typeof length == 'number') {
+ var result = Array(length);
+ while (++index < length) {
+ result[index] = callback(collection[index], index, collection);
+ }
+ } else {
+ result = [];
+ forOwn(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the maximum value of a collection. If the collection is empty or
+ * falsey `-Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.max(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'fred', 'age': 40 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.max(characters, 'age');
+ * // => { 'name': 'fred', 'age': 40 };
+ */
+ function max(collection, callback, thisArg) {
+ var computed = -Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (callback == null && typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = createCallback(callback, thisArg, 3);
+
+ forEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current > computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the minimum value of a collection. If the collection is empty or
+ * falsey `Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.min(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'barney', 'age': 36 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.min(characters, 'age');
+ * // => { 'name': 'barney', 'age': 36 };
+ */
+ function min(collection, callback, thisArg) {
+ var computed = Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (callback == null && typeof length == 'number') {
+ while (++index < length) {
+ var value = collection[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = createCallback(callback, thisArg, 3);
+
+ forEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current < computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the value of a specified property from all elements in the collection.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {string} property The name of the property to pluck.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.pluck(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ var pluck = map;
+
+ /**
+ * Reduces a collection to a value which is the accumulated result of running
+ * each element in the collection through the callback, where each successive
+ * callback execution consumes the return value of the previous execution. If
+ * `accumulator` is not provided the first element of the collection will be
+ * used as the initial `accumulator` value. The callback is bound to `thisArg`
+ * and invoked with four arguments; (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function reduce(collection, callback, accumulator, thisArg) {
+ if (!collection) return accumulator;
+ var noaccum = arguments.length < 3;
+ callback = createCallback(callback, thisArg, 4);
+
+ var index = -1,
+ length = collection.length;
+
+ if (typeof length == 'number') {
+ if (noaccum) {
+ accumulator = collection[++index];
+ }
+ while (++index < length) {
+ accumulator = callback(accumulator, collection[index], index, collection);
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection)
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * This method is like `_.reduce` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var list = [[0, 1], [2, 3], [4, 5]];
+ * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = createCallback(callback, thisArg, 4);
+ forEachRight(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The opposite of `_.filter` this method returns the elements of a
+ * collection that the callback does **not** return truey for.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that failed the callback check.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [1, 3, 5]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.reject(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ *
+ * // using "_.where" callback shorthand
+ * _.reject(characters, { 'age': 36 });
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ */
+ function reject(collection, callback, thisArg) {
+ callback = createCallback(callback, thisArg, 3);
+ return filter(collection, function(value, index, collection) {
+ return !callback(value, index, collection);
+ });
+ }
+
+ /**
+ * Retrieves a random element or `n` random elements from a collection.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to sample.
+ * @param {number} [n] The number of elements to sample.
+ * @param- {Object} [guard] Allows working with functions like `_.map`
+ * without using their `index` arguments as `n`.
+ * @returns {Array} Returns the random sample(s) of `collection`.
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ *
+ * _.sample([1, 2, 3, 4], 2);
+ * // => [3, 1]
+ */
+ function sample(collection, n, guard) {
+ if (collection && typeof collection.length != 'number') {
+ collection = values(collection);
+ }
+ if (n == null || guard) {
+ return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+ }
+ var result = shuffle(collection);
+ result.length = nativeMin(nativeMax(0, n), result.length);
+ return result;
+ }
+
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(collection) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ var rand = baseRandom(0, ++index);
+ result[index] = result[rand];
+ result[rand] = value;
+ });
+ return result;
+ }
+
+ /**
+ * Gets the size of the `collection` by returning `collection.length` for arrays
+ * and array-like objects or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns `collection.length` or number of own enumerable properties.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return typeof length == 'number' ? length : keys(collection).length;
+ }
+
+ /**
+ * Checks if the callback returns a truey value for **any** element of a
+ * collection. The function returns as soon as it finds a passing value and
+ * does not iterate over the entire collection. The callback is bound to
+ * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if any element passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.some(characters, 'blocked');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.some(characters, { 'age': 1 });
+ * // => false
+ */
+ function some(collection, callback, thisArg) {
+ var result;
+ callback = createCallback(callback, thisArg, 3);
+
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ if (typeof length == 'number') {
+ while (++index < length) {
+ if ((result = callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ forOwn(collection, function(value, index, collection) {
+ return (result = callback(value, index, collection)) && indicatorObject;
+ });
+ }
+ return !!result;
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through the callback. This method
+ * performs a stable sort, that is, it will preserve the original sort order
+ * of equal elements. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an array of property names is provided for `callback` the collection
+ * will be sorted by each property value.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of sorted elements.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+ * // => [3, 1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 26 },
+ * { 'name': 'fred', 'age': 30 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(_.sortBy(characters, 'age'), _.values);
+ * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+ *
+ * // sorting by multiple properties
+ * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+ * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+ */
+ function sortBy(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ callback = createCallback(callback, thisArg, 3);
+ forEach(collection, function(value, key, collection) {
+ result[++index] = {
+ 'criteria': [callback(value, key, collection)],
+ 'index': index,
+ 'value': value
+ };
+ });
+
+ length = result.length;
+ result.sort(compareAscending);
+ while (length--) {
+ result[length] = result[length].value;
+ }
+ return result;
+ }
+
+ /**
+ * Converts the `collection` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to convert.
+ * @returns {Array} Returns the new converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+ * // => [2, 3, 4]
+ */
+ function toArray(collection) {
+ if (isArray(collection)) {
+ return slice(collection);
+ }
+ if (collection && typeof collection.length == 'number') {
+ return map(collection);
+ }
+ return values(collection);
+ }
+
+ /**
+ * Performs a deep comparison of each element in a `collection` to the given
+ * `properties` object, returning an array of all elements that have equivalent
+ * property values.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Object} props The object of property values to filter by.
+ * @returns {Array} Returns a new array of elements that have the given properties.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * _.where(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+ *
+ * _.where(characters, { 'pets': ['dino'] });
+ * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+ */
+ function where(collection, properties, first) {
+ return (first && isEmpty(properties))
+ ? undefined
+ : (first ? find : filter)(collection, properties);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are all falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all values of the provided arrays using strict
+ * equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {...Array} [values] The arrays of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ * // => [1, 3, 4]
+ */
+ function difference(array) {
+ return baseDifference(array, baseFlatten(arguments, true, true, 1));
+ }
+
+ /**
+ * Gets the first element or first `n` elements of an array. If a callback
+ * is provided elements at the beginning of the array are returned as long
+ * as the callback returns truey. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head, take
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the first element(s) of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.first([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.first(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function first(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = -1;
+ callback = createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[0] : undefined;
+ }
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, n), length));
+ }
+
+ /**
+ * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+ * is truey, the array will only be flattened a single level. If a callback
+ * is provided each element of the array is passed through the callback before
+ * flattening. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ *
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.flatten(characters, 'pets');
+ * // => ['hoppy', 'baby puss', 'dino']
+ */
+ function flatten(array, isShallow) {
+ return baseFlatten(array, isShallow);
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found using
+ * strict equality for comparisons, i.e. `===`. If the array is already sorted
+ * providing `true` for `fromIndex` will run a faster binary search.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ if (typeof fromIndex == 'number') {
+ var length = array ? array.length : 0;
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+ } else if (fromIndex) {
+ var index = sortedIndex(array, value);
+ return array[index] === value ? index : -1;
+ }
+ return baseIndexOf(array, value, fromIndex);
+ }
+
+ /**
+ * Gets all but the last element or last `n` elements of an array. If a
+ * callback is provided elements at the end of the array are excluded from
+ * the result as long as the callback returns truey. The callback is bound
+ * to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.initial([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.initial([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.initial(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function initial(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : callback || n;
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+ }
+
+ /**
+ * Creates an array of unique values present in all provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of shared values.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection() {
+ var args = [],
+ argsIndex = -1,
+ argsLength = arguments.length;
+
+ while (++argsIndex < argsLength) {
+ var value = arguments[argsIndex];
+ if (isArray(value) || isArguments(value)) {
+ args.push(value);
+ }
+ }
+ var array = args[0],
+ index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [];
+
+ outer:
+ while (++index < length) {
+ value = array[index];
+ if (indexOf(result, value) < 0) {
+ var argsIndex = argsLength;
+ while (--argsIndex) {
+ if (indexOf(args[argsIndex], value) < 0) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets the last element or last `n` elements of an array. If a callback is
+ * provided elements at the end of the array are returned as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the last element(s) of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ *
+ * _.last([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.last([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.last(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.last(characters, { 'employer': 'na' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function last(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[length - 1] : undefined;
+ }
+ }
+ return slice(array, nativeMax(0, length - n));
+ }
+
+ /**
+ * Gets the index at which the last occurrence of `value` is found using strict
+ * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+ * as the offset from the end of the collection.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var index = array ? array.length : 0;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`. If `start` is less than `stop` a
+ * zero-length range is created unless a negative `step` is specified.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns a new range array.
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = (+step || 1);
+
+ if (end == null) {
+ end = start;
+ start = 0;
+ }
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(0, ceil((end - start) / step)),
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
+ }
+
+ /**
+ * The opposite of `_.initial` this method gets all but the first element or
+ * first `n` elements of an array. If a callback function is provided elements
+ * at the beginning of the array are excluded from the result as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias drop, tail
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.rest([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.rest([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.rest(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.rest(characters, { 'employer': 'slate' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function rest(array, callback, thisArg) {
+ if (typeof callback != 'number' && callback != null) {
+ var n = 0,
+ index = -1,
+ length = array ? array.length : 0;
+
+ callback = createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+ }
+ return slice(array, n);
+ }
+
+ /**
+ * Uses a binary search to determine the smallest index at which a value
+ * should be inserted into a given sorted array in order to maintain the sort
+ * order of the array. If a callback is provided it will be executed for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([20, 30, 50], 40);
+ * // => 2
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 2
+ *
+ * var dict = {
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * };
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return dict.wordToNumber[word];
+ * });
+ * // => 2
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return this.wordToNumber[word];
+ * }, dict);
+ * // => 2
+ */
+ function sortedIndex(array, value, callback, thisArg) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ // explicitly reference `identity` for better inlining in Firefox
+ callback = callback ? createCallback(callback, thisArg, 1) : identity;
+ value = callback(value);
+
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ (callback(array[mid]) < value)
+ ? low = mid + 1
+ : high = mid;
+ }
+ return low;
+ }
+
+ /**
+ * Creates an array of unique values, in order, of the provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of combined values.
+ * @example
+ *
+ * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2, 3, 5, 4]
+ */
+ function union() {
+ return baseUniq(baseFlatten(arguments, true, true));
+ }
+
+ /**
+ * Creates a duplicate-value-free version of an array using strict equality
+ * for comparisons, i.e. `===`. If the array is sorted, providing
+ * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+ * each element of `array` is passed through the callback before uniqueness
+ * is computed. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1, 3, 1]);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 1, 2, 2, 3], true);
+ * // => [1, 2, 3]
+ *
+ * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+ * // => ['A', 'b', 'C']
+ *
+ * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+ * // => [1, 2.5, 3]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, callback, thisArg) {
+ // juggle arguments
+ if (typeof isSorted != 'boolean' && isSorted != null) {
+ thisArg = callback;
+ callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+ isSorted = false;
+ }
+ if (callback != null) {
+ callback = createCallback(callback, thisArg, 3);
+ }
+ return baseUniq(array, isSorted, callback);
+ }
+
+ /**
+ * Creates an array excluding all provided values using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to filter.
+ * @param {...*} [value] The values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ return baseDifference(array, slice(arguments, 1));
+ }
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the first
+ * elements of the given arrays, the second of which contains the second
+ * elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @alias unzip
+ * @category Arrays
+ * @param {...Array} [array] Arrays to process.
+ * @returns {Array} Returns a new array of grouped elements.
+ * @example
+ *
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ */
+ function zip() {
+ var index = -1,
+ length = max(pluck(arguments, 'length')),
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = pluck(arguments, index);
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed from arrays of `keys` and `values`. Provide
+ * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+ * or two arrays, one of `keys` and one of corresponding `values`.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Arrays
+ * @param {Array} keys The array of keys.
+ * @param {Array} [values=[]] The array of values.
+ * @returns {Object} Returns an object composed of the given keys and
+ * corresponding values.
+ * @example
+ *
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ function zipObject(keys, values) {
+ var index = -1,
+ length = keys ? keys.length : 0,
+ result = {};
+
+ if (!values && length && !isArray(keys[0])) {
+ values = [];
+ }
+ while (++index < length) {
+ var key = keys[index];
+ if (values) {
+ result[key] = values[index];
+ } else if (key) {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that executes `func`, with the `this` binding and
+ * arguments of the created function, only after being called `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {number} n The number of times the function must be called before
+ * `func` is executed.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('Done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => logs 'Done saving!', after all saves have completed
+ */
+ function after(n, func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with the `this`
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * provided to the bound function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var func = function(greeting) {
+ * return greeting + ' ' + this.name;
+ * };
+ *
+ * func = _.bind(func, { 'name': 'fred' }, 'hi');
+ * func();
+ * // => 'hi fred'
+ */
+ function bind(func, thisArg) {
+ return arguments.length > 2
+ ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+ : createWrapper(func, 1, null, null, thisArg);
+ }
+
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing
+ * method. Method names may be specified as individual arguments or as arrays
+ * of method names. If no method names are provided all the function properties
+ * of `object` will be bound.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {...string} [methodName] The object method names to
+ * bind, specified as individual method names or arrays of method names.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { console.log('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => logs 'clicked docs', when the button is clicked
+ */
+ function bindAll(object) {
+ var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+ index = -1,
+ length = funcs.length;
+
+ while (++index < length) {
+ var key = funcs[index];
+ object[key] = createWrapper(object[key], 1, null, null, object);
+ }
+ return object;
+ }
+
+ /**
+ * Creates a function that is the composition of the provided functions,
+ * where each function consumes the return value of the function that follows.
+ * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * Each function is executed with the `this` binding of the composed function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {...Function} [func] Functions to compose.
+ * @returns {Function} Returns the new composed function.
+ * @example
+ *
+ * var realNameMap = {
+ * 'pebbles': 'penelope'
+ * };
+ *
+ * var format = function(name) {
+ * name = realNameMap[name.toLowerCase()] || name;
+ * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+ * };
+ *
+ * var greet = function(formatted) {
+ * return 'Hiya ' + formatted + '!';
+ * };
+ *
+ * var welcome = _.compose(greet, format);
+ * welcome('pebbles');
+ * // => 'Hiya Penelope!'
+ */
+ function compose() {
+ var funcs = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ if (!isFunction(funcs[length])) {
+ throw new TypeError;
+ }
+ }
+ return function() {
+ var args = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ args = [funcs[length].apply(this, args)];
+ }
+ return args[0];
+ };
+ }
+
+ /**
+ * Creates a function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked.
+ * Provide an options object to indicate that `func` should be invoked on
+ * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+ * to the debounced function will return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the debounced function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to delay.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+ * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // avoid costly calculations while the window size is in flux
+ * var lazyLayout = _.debounce(calculateLayout, 150);
+ * jQuery(window).on('resize', lazyLayout);
+ *
+ * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+ * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * });
+ *
+ * // ensure `batchLog` is executed once after 1 second of debounced calls
+ * var source = new EventSource('/stream');
+ * source.addEventListener('message', _.debounce(batchLog, 250, {
+ * 'maxWait': 1000
+ * }, false);
+ */
+ function debounce(func, wait, options) {
+ var args,
+ maxTimeoutId,
+ result,
+ stamp,
+ thisArg,
+ timeoutId,
+ trailingCall,
+ lastCalled = 0,
+ maxWait = false,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ wait = nativeMax(0, wait) || 0;
+ if (options === true) {
+ var leading = true;
+ trailing = false;
+ } else if (isObject(options)) {
+ leading = options.leading;
+ maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ var delayed = function() {
+ var remaining = wait - (now() - stamp);
+ if (remaining <= 0) {
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ var isCalled = trailingCall;
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (isCalled) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ } else {
+ timeoutId = setTimeout(delayed, remaining);
+ }
+ };
+
+ var maxDelayed = function() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (trailing || (maxWait !== wait)) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ };
+
+ return function() {
+ args = arguments;
+ stamp = now();
+ thisArg = this;
+ trailingCall = trailing && (timeoutId || !leading);
+
+ if (maxWait === false) {
+ var leadingCall = leading && !timeoutId;
+ } else {
+ if (!maxTimeoutId && !leading) {
+ lastCalled = stamp;
+ }
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0;
+
+ if (isCalled) {
+ if (maxTimeoutId) {
+ maxTimeoutId = clearTimeout(maxTimeoutId);
+ }
+ lastCalled = stamp;
+ result = func.apply(thisArg, args);
+ }
+ else if (!maxTimeoutId) {
+ maxTimeoutId = setTimeout(maxDelayed, remaining);
+ }
+ }
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
+ timeoutId = setTimeout(delayed, wait);
+ }
+ if (leadingCall) {
+ isCalled = true;
+ result = func.apply(thisArg, args);
+ }
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Defers executing the `func` function until the current call stack has cleared.
+ * Additional arguments will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to defer.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) { console.log(text); }, 'deferred');
+ * // logs 'deferred' after one or more milliseconds
+ */
+ function defer(func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 1);
+ return setTimeout(function() { func.apply(undefined, args); }, 1);
+ }
+
+ /**
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay execution.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) { console.log(text); }, 1000, 'later');
+ * // => logs 'later' after one second
+ */
+ function delay(func, wait) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 2);
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided it will be used to determine the cache key for storing the result
+ * based on the arguments provided to the memoized function. By default, the
+ * first argument provided to the memoized function is used as the cache key.
+ * The `func` is executed with the `this` binding of the memoized function.
+ * The result cache is exposed as the `cache` property on the memoized function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] A function used to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var fibonacci = _.memoize(function(n) {
+ * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ * });
+ *
+ * fibonacci(9)
+ * // => 34
+ *
+ * var data = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // modifying the result cache
+ * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+ * get('pebbles');
+ * // => { 'name': 'pebbles', 'age': 1 }
+ *
+ * get.cache.pebbles.name = 'penelope';
+ * get('pebbles');
+ * // => { 'name': 'penelope', 'age': 1 }
+ */
+ function memoize(func, resolver) {
+ var cache = {};
+ return function() {
+ var key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+ return hasOwnProperty.call(cache, key)
+ ? cache[key]
+ : (cache[key] = func.apply(this, arguments));
+ };
+ }
+
+ /**
+ * Creates a function that is restricted to execute `func` once. Repeat calls to
+ * the function will return the value of the first call. The `func` is executed
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` executes `createApplication` once
+ */
+ function once(func) {
+ var ran,
+ result;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (ran) {
+ return result;
+ }
+ ran = true;
+ result = func.apply(this, arguments);
+
+ // clear the `func` variable so the function may be garbage collected
+ func = null;
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those provided to the new function. This
+ * method is similar to `_.bind` except it does **not** alter the `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ' ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('fred');
+ * // => 'hi fred'
+ */
+ function partial(func) {
+ return createWrapper(func, 16, slice(arguments, 1));
+ }
+
+ /**
+ * Creates a function that, when executed, will only call the `func` function
+ * at most once per every `wait` milliseconds. Provide an options object to
+ * indicate that `func` should be invoked on the leading and/or trailing edge
+ * of the `wait` timeout. Subsequent calls to the throttled function will
+ * return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the throttled function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to throttle.
+ * @param {number} wait The number of milliseconds to throttle executions to.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // avoid excessively updating the position while scrolling
+ * var throttled = _.throttle(updatePosition, 100);
+ * jQuery(window).on('scroll', throttled);
+ *
+ * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+ * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+ * 'trailing': false
+ * }));
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ if (options === false) {
+ leading = false;
+ } else if (isObject(options)) {
+ leading = 'leading' in options ? options.leading : leading;
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ options = {};
+ options.leading = leading;
+ options.maxWait = wait;
+ options.trailing = trailing;
+
+ return debounce(func, wait, options);
+ }
+
+ /**
+ * Creates a function that provides `value` to the wrapper function as its
+ * first argument. Additional arguments provided to the function are appended
+ * to those provided to the wrapper function. The wrapper is executed with
+ * the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {*} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '<p>' + func(text) + '</p>';
+ * });
+ *
+ * p('Fred, Wilma, & Pebbles');
+ * // => '<p>Fred, Wilma, & Pebbles</p>'
+ */
+ function wrap(value, wrapper) {
+ return createWrapper(wrapper, 16, [value]);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Produces a callback bound to an optional `thisArg`. If `func` is a property
+ * name the created callback will return the property value for a given element.
+ * If `func` is an object the created callback will return `true` for elements
+ * that contain the equivalent object properties, otherwise it will return `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // wrap to create custom callback shorthands
+ * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+ * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+ * return !match ? func(callback, thisArg) : function(object) {
+ * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+ * };
+ * });
+ *
+ * _.filter(characters, 'age__gt38');
+ * // => [{ 'name': 'fred', 'age': 40 }]
+ */
+ function createCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (func == null || type == 'function') {
+ return baseCreateCallback(func, thisArg, argCount);
+ }
+ // handle "_.pluck" style callback shorthands
+ if (type != 'object') {
+ return property(func);
+ }
+ var props = keys(func);
+ return function(object) {
+ var length = props.length,
+ result = false;
+
+ while (length--) {
+ if (!(result = object[props[length]] === func[props[length]])) {
+ break;
+ }
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding HTML entities.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles'
+ */
+ function escape(string) {
+ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+ }
+
+ /**
+ * This method returns the first argument provided to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.identity(object) === object;
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
+
+ /**
+ * Adds function properties of a source object to the destination object.
+ * If `object` is a function methods will be added to its prototype as well.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Function|Object} [object=lodash] object The destination object.
+ * @param {Object} source The object of functions to add.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+ * @example
+ *
+ * function capitalize(string) {
+ * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+ * }
+ *
+ * _.mixin({ 'capitalize': capitalize });
+ * _.capitalize('fred');
+ * // => 'Fred'
+ *
+ * _('fred').capitalize().value();
+ * // => 'Fred'
+ *
+ * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+ * _('fred').capitalize();
+ * // => 'Fred'
+ */
+ function mixin(object) {
+ forEach(functions(object), function(methodName) {
+ var func = lodash[methodName] = object[methodName];
+
+ lodash.prototype[methodName] = function() {
+ var args = [this.__wrapped__];
+ push.apply(args, arguments);
+
+ var result = func.apply(lodash, args);
+ return this.__chain__
+ ? new lodashWrapper(result, true)
+ : result;
+ };
+ });
+ }
+
+ /**
+ * Reverts the '_' variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ root._ = oldDash;
+ return this;
+ }
+
+ /**
+ * A no-operation function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.noop(object) === undefined;
+ * // => true
+ */
+ function noop() {
+ // no operation performed
+ }
+
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var stamp = _.now();
+ * _.defer(function() { console.log(_.now() - stamp); });
+ * // => logs the number of milliseconds it took for the deferred function to be called
+ */
+ var now = isNative(now = Date.now) && now || function() {
+ return new Date().getTime();
+ };
+
+ /**
+ * Creates a "_.pluck" style function, which returns the `key` value of a
+ * given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} key The name of the property to retrieve.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 36 }
+ * ];
+ *
+ * var getName = _.property('name');
+ *
+ * _.map(characters, getName);
+ * // => ['barney', 'fred']
+ *
+ * _.sortBy(characters, getName);
+ * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
+ */
+ function property(key) {
+ return function(object) {
+ return object[key];
+ };
+ }
+
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is provided a number between `0` and the given number will be
+ * returned. If `floating` is truey or either `min` or `max` are floats a
+ * floating-point number will be returned instead of an integer.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} [min=0] The minimum possible value.
+ * @param {number} [max=1] The maximum possible value.
+ * @param {boolean} [floating=false] Specify returning a floating-point number.
+ * @returns {number} Returns a random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(min, max) {
+ if (min == null && max == null) {
+ max = 1;
+ }
+ min = +min || 0;
+ if (max == null) {
+ max = min;
+ min = 0;
+ } else {
+ max = +max || 0;
+ }
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * Resolves the value of property `key` on `object`. If `key` is a function
+ * it will be invoked with the `this` binding of `object` and its result returned,
+ * else the property value is returned. If `object` is falsey then `undefined`
+ * is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to resolve.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = {
+ * 'cheese': 'crumpets',
+ * 'stuff': function() {
+ * return 'nonsense';
+ * }
+ * };
+ *
+ * _.result(object, 'cheese');
+ * // => 'crumpets'
+ *
+ * _.result(object, 'stuff');
+ * // => 'nonsense'
+ */
+ function result(object, key) {
+ if (object) {
+ var value = object[key];
+ return isFunction(value) ? object[key]() : value;
+ }
+ }
+
+ /**
+ * A micro-templating method that handles arbitrary delimiters, preserves
+ * whitespace, and correctly escapes quotes within interpolated code.
+ *
+ * Note: In the development build, `_.template` utilizes sourceURLs for easier
+ * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ *
+ * For more information on precompiling templates see:
+ * https://lodash.com/custom-builds
+ *
+ * For more information on Chrome extension sandboxes see:
+ * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} text The template text.
+ * @param {Object} data The data object used to populate the text.
+ * @param {Object} [options] The options object.
+ * @param {RegExp} [options.escape] The "escape" delimiter.
+ * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+ * @param {Object} [options.imports] An object to import into the template as local variables.
+ * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+ * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [variable] The data object variable name.
+ * @returns {Function|string} Returns a compiled function when no `data` object
+ * is given, else it returns the interpolated text.
+ * @example
+ *
+ * // using the "interpolate" delimiter to create a compiled template
+ * var compiled = _.template('hello <%= name %>');
+ * compiled({ 'name': 'fred' });
+ * // => 'hello fred'
+ *
+ * // using the "escape" delimiter to escape HTML in data property values
+ * _.template('<b><%- value %></b>', { 'value': '<script>' });
+ * // => '<b><script></b>'
+ *
+ * // using the "evaluate" delimiter to generate HTML
+ * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+ * _.template('hello ${ name }', { 'name': 'pebbles' });
+ * // => 'hello pebbles'
+ *
+ * // using the internal `print` function in "evaluate" delimiters
+ * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+ * // => 'hello barney!'
+ *
+ * // using a custom template delimiters
+ * _.templateSettings = {
+ * 'interpolate': /{{([\s\S]+?)}}/g
+ * };
+ *
+ * _.template('hello {{ name }}!', { 'name': 'mustache' });
+ * // => 'hello mustache!'
+ *
+ * // using the `imports` option to import jQuery
+ * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the `sourceURL` option to specify a custom sourceURL for the template
+ * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+ * compiled(data);
+ * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+ *
+ * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+ * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+ * compiled.source;
+ * // => function(data) {
+ * var __t, __p = '', __e = _.escape;
+ * __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+ * return __p;
+ * }
+ *
+ * // using the `source` property to inline compiled templates for meaningful
+ * // line numbers in error messages and a stack trace
+ * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+ * var JST = {\
+ * "main": ' + _.template(mainText).source + '\
+ * };\
+ * ');
+ */
+ function template(text, data, options) {
+ var _ = lodash,
+ settings = _.templateSettings;
+
+ text = String(text || '');
+ options = defaults({}, options, settings);
+
+ var index = 0,
+ source = "__p += '",
+ variable = options.variable;
+
+ var reDelimiters = RegExp(
+ (options.escape || reNoMatch).source + '|' +
+ (options.interpolate || reNoMatch).source + '|' +
+ (options.evaluate || reNoMatch).source + '|$'
+ , 'g');
+
+ text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) {
+ source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+ if (escapeValue) {
+ source += "' +\n_.escape(" + escapeValue + ") +\n'";
+ }
+ if (evaluateValue) {
+ source += "';\n" + evaluateValue + ";\n__p += '";
+ }
+ if (interpolateValue) {
+ source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+ }
+ index = offset + match.length;
+ return match;
+ });
+
+ source += "';\n";
+ if (!variable) {
+ variable = 'obj';
+ source = 'with (' + variable + ' || {}) {\n' + source + '\n}\n';
+ }
+ source = 'function(' + variable + ') {\n' +
+ "var __t, __p = '', __j = Array.prototype.join;\n" +
+ "function print() { __p += __j.call(arguments, '') }\n" +
+ source +
+ 'return __p\n}';
+
+ try {
+ var result = Function('_', 'return ' + source)(_);
+ } catch(e) {
+ e.source = source;
+ throw e;
+ }
+ if (data) {
+ return result(data);
+ }
+ result.source = source;
+ return result;
+ }
+
+ /**
+ * Executes the callback `n` times, returning an array of the results
+ * of each callback execution. The callback is bound to `thisArg` and invoked
+ * with one argument; (index).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} n The number of times to execute the callback.
+ * @param {Function} callback The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns an array of the results of each `callback` execution.
+ * @example
+ *
+ * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+ * // => [3, 6, 4]
+ *
+ * _.times(3, function(n) { mage.castSpell(n); });
+ * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+ *
+ * _.times(3, function(n) { this.cast(n); }, mage);
+ * // => also calls `mage.castSpell(n)` three times
+ */
+ function times(n, callback, thisArg) {
+ n = (n = +n) > -1 ? n : 0;
+ var index = -1,
+ result = Array(n);
+
+ callback = baseCreateCallback(callback, thisArg, 1);
+ while (++index < n) {
+ result[index] = callback(index);
+ }
+ return result;
+ }
+
+ /**
+ * The inverse of `_.escape` this method converts the HTML entities
+ * `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding characters.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to unescape.
+ * @returns {string} Returns the unescaped string.
+ * @example
+ *
+ * _.unescape('Fred, Barney & Pebbles');
+ * // => 'Fred, Barney & Pebbles'
+ */
+ function unescape(string) {
+ return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+ }
+
+ /**
+ * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} [prefix] The value to prefix the ID with.
+ * @returns {string} Returns the unique ID.
+ * @example
+ *
+ * _.uniqueId('contact_');
+ * // => 'contact_104'
+ *
+ * _.uniqueId();
+ * // => '105'
+ */
+ function uniqueId(prefix) {
+ var id = ++idCounter + '';
+ return prefix ? prefix + id : id;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object that wraps the given value with explicit
+ * method chaining enabled.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _.chain(characters)
+ * .sortBy('age')
+ * .map(function(chr) { return chr.name + ' is ' + chr.age; })
+ * .first()
+ * .value();
+ * // => 'pebbles is 1'
+ */
+ function chain(value) {
+ value = new lodashWrapper(value);
+ value.__chain__ = true;
+ return value;
+ }
+
+ /**
+ * Invokes `interceptor` with the `value` as the first argument and then
+ * returns `value`. The purpose of this method is to "tap into" a method
+ * chain in order to perform operations on intermediate results within
+ * the chain.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3, 4])
+ * .tap(function(array) { array.pop(); })
+ * .reverse()
+ * .value();
+ * // => [3, 2, 1]
+ */
+ function tap(value, interceptor) {
+ interceptor(value);
+ return value;
+ }
+
+ /**
+ * Enables explicit method chaining on the wrapper object.
+ *
+ * @name chain
+ * @memberOf _
+ * @category Chaining
+ * @returns {*} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // without explicit chaining
+ * _(characters).first();
+ * // => { 'name': 'barney', 'age': 36 }
+ *
+ * // with explicit chaining
+ * _(characters).chain()
+ * .first()
+ * .pick('age')
+ * .value();
+ * // => { 'age': 36 }
+ */
+ function wrapperChain() {
+ this.__chain__ = true;
+ return this;
+ }
+
+ /**
+ * Extracts the wrapped value.
+ *
+ * @name valueOf
+ * @memberOf _
+ * @alias value
+ * @category Chaining
+ * @returns {*} Returns the wrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).valueOf();
+ * // => [1, 2, 3]
+ */
+ function wrapperValueOf() {
+ return this.__wrapped__;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return wrapped values when chaining
+ lodash.after = after;
+ lodash.bind = bind;
+ lodash.bindAll = bindAll;
+ lodash.chain = chain;
+ lodash.compact = compact;
+ lodash.compose = compose;
+ lodash.countBy = countBy;
+ lodash.debounce = debounce;
+ lodash.defaults = defaults;
+ lodash.defer = defer;
+ lodash.delay = delay;
+ lodash.difference = difference;
+ lodash.filter = filter;
+ lodash.flatten = flatten;
+ lodash.forEach = forEach;
+ lodash.functions = functions;
+ lodash.groupBy = groupBy;
+ lodash.indexBy = indexBy;
+ lodash.initial = initial;
+ lodash.intersection = intersection;
+ lodash.invert = invert;
+ lodash.invoke = invoke;
+ lodash.keys = keys;
+ lodash.map = map;
+ lodash.max = max;
+ lodash.memoize = memoize;
+ lodash.min = min;
+ lodash.omit = omit;
+ lodash.once = once;
+ lodash.pairs = pairs;
+ lodash.partial = partial;
+ lodash.pick = pick;
+ lodash.pluck = pluck;
+ lodash.range = range;
+ lodash.reject = reject;
+ lodash.rest = rest;
+ lodash.shuffle = shuffle;
+ lodash.sortBy = sortBy;
+ lodash.tap = tap;
+ lodash.throttle = throttle;
+ lodash.times = times;
+ lodash.toArray = toArray;
+ lodash.union = union;
+ lodash.uniq = uniq;
+ lodash.values = values;
+ lodash.where = where;
+ lodash.without = without;
+ lodash.wrap = wrap;
+ lodash.zip = zip;
+
+ // add aliases
+ lodash.collect = map;
+ lodash.drop = rest;
+ lodash.each = forEach;
+ lodash.extend = assign;
+ lodash.methods = functions;
+ lodash.object = zipObject;
+ lodash.select = filter;
+ lodash.tail = rest;
+ lodash.unique = uniq;
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return unwrapped values when chaining
+ lodash.clone = clone;
+ lodash.contains = contains;
+ lodash.escape = escape;
+ lodash.every = every;
+ lodash.find = find;
+ lodash.has = has;
+ lodash.identity = identity;
+ lodash.indexOf = indexOf;
+ lodash.isArguments = isArguments;
+ lodash.isArray = isArray;
+ lodash.isBoolean = isBoolean;
+ lodash.isDate = isDate;
+ lodash.isElement = isElement;
+ lodash.isEmpty = isEmpty;
+ lodash.isEqual = isEqual;
+ lodash.isFinite = isFinite;
+ lodash.isFunction = isFunction;
+ lodash.isNaN = isNaN;
+ lodash.isNull = isNull;
+ lodash.isNumber = isNumber;
+ lodash.isObject = isObject;
+ lodash.isRegExp = isRegExp;
+ lodash.isString = isString;
+ lodash.isUndefined = isUndefined;
+ lodash.lastIndexOf = lastIndexOf;
+ lodash.mixin = mixin;
+ lodash.noConflict = noConflict;
+ lodash.random = random;
+ lodash.reduce = reduce;
+ lodash.reduceRight = reduceRight;
+ lodash.result = result;
+ lodash.size = size;
+ lodash.some = some;
+ lodash.sortedIndex = sortedIndex;
+ lodash.template = template;
+ lodash.unescape = unescape;
+ lodash.uniqueId = uniqueId;
+
+ // add aliases
+ lodash.all = every;
+ lodash.any = some;
+ lodash.detect = find;
+ lodash.findWhere = findWhere;
+ lodash.foldl = reduce;
+ lodash.foldr = reduceRight;
+ lodash.include = contains;
+ lodash.inject = reduce;
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions capable of returning wrapped and unwrapped values when chaining
+ lodash.first = first;
+ lodash.last = last;
+ lodash.sample = sample;
+
+ // add aliases
+ lodash.take = first;
+ lodash.head = first;
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions to `lodash.prototype`
+ mixin(lodash);
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf _
+ * @type string
+ */
+ lodash.VERSION = '2.4.2';
+
+ // add "Chaining" functions to the wrapper
+ lodash.prototype.chain = wrapperChain;
+ lodash.prototype.value = wrapperValueOf;
+
+ // add `Array` mutator functions to the wrapper
+ forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ var value = this.__wrapped__;
+ func.apply(value, arguments);
+
+ // avoid array-like object bugs with `Array#shift` and `Array#splice`
+ // in Firefox < 10 and IE < 9
+ if (!support.spliceObjects && value.length === 0) {
+ delete value[0];
+ }
+ return this;
+ };
+ });
+
+ // add `Array` accessor functions to the wrapper
+ forEach(['concat', 'join', 'slice'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ var value = this.__wrapped__,
+ result = func.apply(value, arguments);
+
+ if (this.__chain__) {
+ result = new lodashWrapper(result);
+ result.__chain__ = true;
+ }
+ return result;
+ };
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ // some AMD build optimizers like r.js check for condition patterns like the following:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // Expose Lo-Dash to the global object even when an AMD loader is present in
+ // case Lo-Dash is loaded with a RequireJS shim config.
+ // See http://requirejs.org/docs/api.html#config-shim
+ root._ = lodash;
+
+ // define as an anonymous module so, through path mapping, it can be
+ // referenced as the "underscore" module
+ define(function() {
+ return lodash;
+ });
+ }
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ else if (freeExports && freeModule) {
+ // in Node.js or RingoJS
+ if (moduleExports) {
+ (freeModule.exports = lodash)._ = lodash;
+ }
+ // in Narwhal or Rhino -require
+ else {
+ freeExports._ = lodash;
+ }
+ }
+ else {
+ // in a browser or Rhino
+ root._ = lodash;
+ }
+}.call(this));
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE
+ * Build: `lodash underscore exports="amd,commonjs,global,node" -o ./dist/lodash.underscore.js`
+ */
+;(function(){function n(n,r,t){t=(t||0)-1;for(var e=n?n.length:0;++t<e;)if(n[t]===r)return t;return-1}function r(n,r){for(var t=n.m,e=r.m,u=-1,o=t.length;++u<o;){var i=t[u],f=e[u];if(i!==f){if(i>f||typeof i=="undefined")return 1;if(i<f||typeof f=="undefined")return-1}}return n.n-r.n}function t(n){return"\\"+yr[n]}function e(n,r,t){r||(r=0),typeof t=="undefined"&&(t=n?n.length:0);var e=-1;t=t-r||0;for(var u=Array(0>t?0:t);++e<t;)u[e]=n[r+e];return u}function u(n){return n instanceof u?n:new o(n)}function o(n,r){this.__chain__=!!r,this.__wrapped__=n
+}function i(n){function r(){if(u){var n=e(u);Rr.apply(n,arguments)}if(this instanceof r){var i=f(t.prototype),n=t.apply(i,n||arguments);return O(n)?n:i}return t.apply(o,n||arguments)}var t=n[0],u=n[2],o=n[4];return r}function f(n){return O(n)?Br(n):{}}function a(n,r,t){if(typeof n!="function")return Y;if(typeof r=="undefined"||!("prototype"in n))return n;switch(t){case 1:return function(t){return n.call(r,t)};case 2:return function(t,e){return n.call(r,t,e)};case 3:return function(t,e,u){return n.call(r,t,e,u)
+};case 4:return function(t,e,u,o){return n.call(r,t,e,u,o)}}return L(n,r)}function l(n){function r(){var n=p?a:this;if(o){var y=e(o);Rr.apply(y,arguments)}return(i||g)&&(y||(y=e(arguments)),i&&Rr.apply(y,i),g&&y.length<c)?(u|=16,l([t,h?u:-4&u,y,null,a,c])):(y||(y=arguments),s&&(t=n[v]),this instanceof r?(n=f(t.prototype),y=t.apply(n,y),O(y)?y:n):t.apply(n,y))}var t=n[0],u=n[1],o=n[2],i=n[3],a=n[4],c=n[5],p=1&u,s=2&u,g=4&u,h=8&u,v=t;return r}function c(n,r){for(var t=-1,e=m(),u=n?n.length:0,o=[];++t<u;){var i=n[t];
+0>e(r,i)&&o.push(i)}return o}function p(n,r,t,e){e=(e||0)-1;for(var u=n?n.length:0,o=[];++e<u;){var i=n[e];if(i&&typeof i=="object"&&typeof i.length=="number"&&(Cr(i)||b(i))){r||(i=p(i,r,t));var f=-1,a=i.length,l=o.length;for(o.length+=a;++f<a;)o[l++]=i[f]}else t||o.push(i)}return o}function s(n,r,t,e){if(n===r)return 0!==n||1/n==1/r;if(n===n&&!(n&&vr[typeof n]||r&&vr[typeof r]))return false;if(null==n||null==r)return n===r;var o=Er.call(n),i=Er.call(r);if(o!=i)return false;switch(o){case lr:case cr:return+n==+r;
+case pr:return n!=+n?r!=+r:0==n?1/n==1/r:n==+r;case gr:case hr:return n==r+""}if(i=o==ar,!i){var f=n instanceof u,a=r instanceof u;if(f||a)return s(f?n.__wrapped__:n,a?r.__wrapped__:r,t,e);if(o!=sr)return false;if(o=n.constructor,f=r.constructor,o!=f&&!(A(o)&&o instanceof o&&A(f)&&f instanceof f)&&"constructor"in n&&"constructor"in r)return false}for(t||(t=[]),e||(e=[]),o=t.length;o--;)if(t[o]==n)return e[o]==r;var l=true,c=0;if(t.push(n),e.push(r),i){if(c=r.length,l=c==n.length)for(;c--&&(l=s(n[c],r[c],t,e)););}else Kr(r,function(r,u,o){return Nr.call(o,u)?(c++,!(l=Nr.call(n,u)&&s(n[u],r,t,e))&&er):void 0
+}),l&&Kr(n,function(n,r,t){return Nr.call(t,r)?!(l=-1<--c)&&er:void 0});return t.pop(),e.pop(),l}function g(n,r,t){for(var e=-1,u=m(),o=n?n.length:0,i=[],f=t?[]:i;++e<o;){var a=n[e],l=t?t(a,e,n):a;(r?!e||f[f.length-1]!==l:0>u(f,l))&&(t&&f.push(l),i.push(a))}return i}function h(n){return function(r,t,e){var u={};t=X(t,e,3),e=-1;var o=r?r.length:0;if(typeof o=="number")for(;++e<o;){var i=r[e];n(u,i,t(i,e,r),r)}else Lr(r,function(r,e,o){n(u,r,t(r,e,o),o)});return u}}function v(n,r,t,e,u,o){var f=16&r,a=32&r;
+if(!(2&r||A(n)))throw new TypeError;return f&&!t.length&&(r&=-17,t=false),a&&!e.length&&(r&=-33,e=false),(1==r||17===r?i:l)([n,r,t,e,u,o])}function y(n){return Vr[n]}function m(){var r=(r=u.indexOf)===G?n:r;return r}function _(n){return typeof n=="function"&&Ar.test(n)}function d(n){return Gr[n]}function b(n){return n&&typeof n=="object"&&typeof n.length=="number"&&Er.call(n)==fr||false}function w(n){if(!n)return n;for(var r=1,t=arguments.length;r<t;r++){var e=arguments[r];if(e)for(var u in e)n[u]=e[u]}return n
+}function j(n){if(!n)return n;for(var r=1,t=arguments.length;r<t;r++){var e=arguments[r];if(e)for(var u in e)"undefined"==typeof n[u]&&(n[u]=e[u])}return n}function x(n){var r=[];return Kr(n,function(n,t){A(n)&&r.push(t)}),r.sort()}function T(n){for(var r=-1,t=Ur(n),e=t.length,u={};++r<e;){var o=t[r];u[n[o]]=o}return u}function E(n){if(!n)return true;if(Cr(n)||N(n))return!n.length;for(var r in n)if(Nr.call(n,r))return false;return true}function A(n){return typeof n=="function"}function O(n){return!(!n||!vr[typeof n])
+}function S(n){return typeof n=="number"||n&&typeof n=="object"&&Er.call(n)==pr||false}function N(n){return typeof n=="string"||n&&typeof n=="object"&&Er.call(n)==hr||false}function R(n){for(var r=-1,t=Ur(n),e=t.length,u=Array(e);++r<e;)u[r]=n[t[r]];return u}function k(n,r){var t=m(),e=n?n.length:0,u=false;return e&&typeof e=="number"?u=-1<t(n,r):Lr(n,function(n){return(u=n===r)&&er}),u}function B(n,r,t){var e=true;r=X(r,t,3),t=-1;var u=n?n.length:0;if(typeof u=="number")for(;++t<u&&(e=!!r(n[t],t,n)););else Lr(n,function(n,t,u){return!(e=!!r(n,t,u))&&er
+});return e}function F(n,r,t){var e=[];r=X(r,t,3),t=-1;var u=n?n.length:0;if(typeof u=="number")for(;++t<u;){var o=n[t];r(o,t,n)&&e.push(o)}else Lr(n,function(n,t,u){r(n,t,u)&&e.push(n)});return e}function q(n,r,t){r=X(r,t,3),t=-1;var e=n?n.length:0;if(typeof e!="number"){var u;return Lr(n,function(n,t,e){return r(n,t,e)?(u=n,er):void 0}),u}for(;++t<e;){var o=n[t];if(r(o,t,n))return o}}function D(n,r,t){var e=-1,u=n?n.length:0;if(r=r&&typeof t=="undefined"?r:a(r,t,3),typeof u=="number")for(;++e<u&&r(n[e],e,n)!==er;);else Lr(n,r)
+}function I(n,r){var t=n?n.length:0;if(typeof t=="number")for(;t--&&false!==r(n[t],t,n););else{var e=Ur(n),t=e.length;Lr(n,function(n,u,o){return u=e?e[--t]:--t,false===r(o[u],u,o)&&er})}}function M(n,r,t){var e=-1,u=n?n.length:0;if(r=X(r,t,3),typeof u=="number")for(var o=Array(u);++e<u;)o[e]=r(n[e],e,n);else o=[],Lr(n,function(n,t,u){o[++e]=r(n,t,u)});return o}function $(n,r,t){var e=-1/0,u=e;typeof r!="function"&&t&&t[r]===n&&(r=null);var o=-1,i=n?n.length:0;if(null==r&&typeof i=="number")for(;++o<i;)t=n[o],t>u&&(u=t);
+else r=X(r,t,3),D(n,function(n,t,o){t=r(n,t,o),t>e&&(e=t,u=n)});return u}function W(n,r,t,e){if(!n)return t;var u=3>arguments.length;r=X(r,e,4);var o=-1,i=n.length;if(typeof i=="number")for(u&&(t=n[++o]);++o<i;)t=r(t,n[o],o,n);else Lr(n,function(n,e,o){t=u?(u=false,n):r(t,n,e,o)});return t}function z(n,r,t,e){var u=3>arguments.length;return r=X(r,e,4),I(n,function(n,e,o){t=u?(u=false,n):r(t,n,e,o)}),t}function C(n){var r=-1,t=n?n.length:0,e=Array(typeof t=="number"?t:0);return D(n,function(n){var t;t=++r,t=0+Sr(Wr()*(t-0+1)),e[r]=e[t],e[t]=n
+}),e}function P(n,r,t){var e;r=X(r,t,3),t=-1;var u=n?n.length:0;if(typeof u=="number")for(;++t<u&&!(e=r(n[t],t,n)););else Lr(n,function(n,t,u){return(e=r(n,t,u))&&er});return!!e}function U(n,r,t){return t&&E(r)?rr:(t?q:F)(n,r)}function V(n,r,t){var u=0,o=n?n.length:0;if(typeof r!="number"&&null!=r){var i=-1;for(r=X(r,t,3);++i<o&&r(n[i],i,n);)u++}else if(u=r,null==u||t)return n?n[0]:rr;return e(n,0,$r(Mr(0,u),o))}function G(r,t,e){if(typeof e=="number"){var u=r?r.length:0;e=0>e?Mr(0,u+e):e||0}else if(e)return e=J(r,t),r[e]===t?e:-1;
+return n(r,t,e)}function H(n,r,t){if(typeof r!="number"&&null!=r){var u=0,o=-1,i=n?n.length:0;for(r=X(r,t,3);++o<i&&r(n[o],o,n);)u++}else u=null==r||t?1:Mr(0,r);return e(n,u)}function J(n,r,t,e){var u=0,o=n?n.length:u;for(t=t?X(t,e,1):Y,r=t(r);u<o;)e=u+o>>>1,t(n[e])<r?u=e+1:o=e;return u}function K(n,r,t,e){return typeof r!="boolean"&&null!=r&&(e=t,t=typeof r!="function"&&e&&e[r]===n?null:r,r=false),null!=t&&(t=X(t,e,3)),g(n,r,t)}function L(n,r){return 2<arguments.length?v(n,17,e(arguments,2),null,r):v(n,1,null,null,r)
+}function Q(n,r,t){var e,u,o,i,f,a,l,c=0,p=false,s=true;if(!A(n))throw new TypeError;if(r=Mr(0,r)||0,true===t)var g=true,s=false;else O(t)&&(g=t.leading,p="maxWait"in t&&(Mr(r,t.maxWait)||0),s="trailing"in t?t.trailing:s);var h=function(){var t=r-(nt()-i);0<t?a=setTimeout(h,t):(u&&clearTimeout(u),t=l,u=a=l=rr,t&&(c=nt(),o=n.apply(f,e),a||u||(e=f=null)))},v=function(){a&&clearTimeout(a),u=a=l=rr,(s||p!==r)&&(c=nt(),o=n.apply(f,e),a||u||(e=f=null))};return function(){if(e=arguments,i=nt(),f=this,l=s&&(a||!g),false===p)var t=g&&!a;
+else{u||g||(c=i);var y=p-(i-c),m=0>=y;m?(u&&(u=clearTimeout(u)),c=i,o=n.apply(f,e)):u||(u=setTimeout(v,y))}return m&&a?a=clearTimeout(a):a||r===p||(a=setTimeout(h,r)),t&&(m=true,o=n.apply(f,e)),!m||a||u||(e=f=null),o}}function X(n,r,t){var e=typeof n;if(null==n||"function"==e)return a(n,r,t);if("object"!=e)return nr(n);var u=Ur(n);return function(r){for(var t=u.length,e=false;t--&&(e=r[u[t]]===n[u[t]]););return e}}function Y(n){return n}function Z(n){D(x(n),function(r){var t=u[r]=n[r];u.prototype[r]=function(){var n=[this.__wrapped__];
+return Rr.apply(n,arguments),n=t.apply(u,n),this.__chain__?new o(n,true):n}})}function nr(n){return function(r){return r[n]}}var rr,tr=0,er={},ur=+new Date+"",or=/($^)/,ir=/['\n\r\t\u2028\u2029\\]/g,fr="[object Arguments]",ar="[object Array]",lr="[object Boolean]",cr="[object Date]",pr="[object Number]",sr="[object Object]",gr="[object RegExp]",hr="[object String]",vr={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},yr={"\\":"\\","'":"'","\n":"n","\r":"r","\t":"t","\u2028":"u2028","\u2029":"u2029"},mr=vr[typeof window]&&window||this,_r=vr[typeof exports]&&exports&&!exports.nodeType&&exports,dr=vr[typeof module]&&module&&!module.nodeType&&module,br=dr&&dr.exports===_r&&_r,wr=vr[typeof global]&&global;
+!wr||wr.global!==wr&&wr.window!==wr||(mr=wr);var jr=[],xr=Object.prototype,Tr=mr._,Er=xr.toString,Ar=RegExp("^"+(Er+"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),Or=Math.ceil,Sr=Math.floor,Nr=xr.hasOwnProperty,Rr=jr.push,kr=xr.propertyIsEnumerable,Br=_(Br=Object.create)&&Br,Fr=_(Fr=Array.isArray)&&Fr,qr=mr.isFinite,Dr=mr.isNaN,Ir=_(Ir=Object.keys)&&Ir,Mr=Math.max,$r=Math.min,Wr=Math.random;o.prototype=u.prototype;var zr={};!function(){var n={0:1,length:1};zr.spliceObjects=(jr.splice.call(n,0,1),!n[0])
+}(1),u.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,variable:""},Br||(f=function(){function n(){}return function(r){if(O(r)){n.prototype=r;var t=new n;n.prototype=null}return t||mr.Object()}}()),b(arguments)||(b=function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&Nr.call(n,"callee")&&!kr.call(n,"callee")||false});var Cr=Fr||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&Er.call(n)==ar||false},Pr=function(n){var r,t=[];
+if(!n||!vr[typeof n])return t;for(r in n)Nr.call(n,r)&&t.push(r);return t},Ur=Ir?function(n){return O(n)?Ir(n):[]}:Pr,Vr={"&":"&","<":"<",">":">",'"':""","'":"'"},Gr=T(Vr),Hr=RegExp("("+Ur(Gr).join("|")+")","g"),Jr=RegExp("["+Ur(Vr).join("")+"]","g"),Kr=function(n,r){var t;if(!n||!vr[typeof n])return n;for(t in n)if(r(n[t],t,n)===er)break;return n},Lr=function(n,r){var t;if(!n||!vr[typeof n])return n;for(t in n)if(Nr.call(n,t)&&r(n[t],t,n)===er)break;return n};A(/x/)&&(A=function(n){return typeof n=="function"&&"[object Function]"==Er.call(n)
+});var Qr=h(function(n,r,t){Nr.call(n,t)?n[t]++:n[t]=1}),Xr=h(function(n,r,t){(Nr.call(n,t)?n[t]:n[t]=[]).push(r)}),Yr=h(function(n,r,t){n[t]=r}),Zr=M,nt=_(nt=Date.now)&&nt||function(){return(new Date).getTime()};u.after=function(n,r){if(!A(r))throw new TypeError;return function(){return 1>--n?r.apply(this,arguments):void 0}},u.bind=L,u.bindAll=function(n){for(var r=1<arguments.length?p(arguments,true,false,1):x(n),t=-1,e=r.length;++t<e;){var u=r[t];n[u]=v(n[u],1,null,null,n)}return n},u.chain=function(n){return n=new o(n),n.__chain__=true,n
+},u.compact=function(n){for(var r=-1,t=n?n.length:0,e=[];++r<t;){var u=n[r];u&&e.push(u)}return e},u.compose=function(){for(var n=arguments,r=n.length;r--;)if(!A(n[r]))throw new TypeError;return function(){for(var r=arguments,t=n.length;t--;)r=[n[t].apply(this,r)];return r[0]}},u.countBy=Qr,u.debounce=Q,u.defaults=j,u.defer=function(n){if(!A(n))throw new TypeError;var r=e(arguments,1);return setTimeout(function(){n.apply(rr,r)},1)},u.delay=function(n,r){if(!A(n))throw new TypeError;var t=e(arguments,2);
+return setTimeout(function(){n.apply(rr,t)},r)},u.difference=function(n){return c(n,p(arguments,true,true,1))},u.filter=F,u.flatten=function(n,r){return p(n,r)},u.forEach=D,u.functions=x,u.groupBy=Xr,u.indexBy=Yr,u.initial=function(n,r,t){var u=0,o=n?n.length:0;if(typeof r!="number"&&null!=r){var i=o;for(r=X(r,t,3);i--&&r(n[i],i,n);)u++}else u=null==r||t?1:r||u;return e(n,0,$r(Mr(0,o-u),o))},u.intersection=function(){for(var n=[],r=-1,t=arguments.length;++r<t;){var e=arguments[r];(Cr(e)||b(e))&&n.push(e)
+}var u=n[0],o=-1,i=m(),f=u?u.length:0,a=[];n:for(;++o<f;)if(e=u[o],0>i(a,e)){for(r=t;--r;)if(0>i(n[r],e))continue n;a.push(e)}return a},u.invert=T,u.invoke=function(n,r){var t=e(arguments,2),u=-1,o=typeof r=="function",i=n?n.length:0,f=Array(typeof i=="number"?i:0);return D(n,function(n){f[++u]=(o?r:n[r]).apply(n,t)}),f},u.keys=Ur,u.map=M,u.max=$,u.memoize=function(n,r){var t={};return function(){var e=r?r.apply(this,arguments):ur+arguments[0];return Nr.call(t,e)?t[e]:t[e]=n.apply(this,arguments)
+}},u.min=function(n,r,t){var e=1/0,u=e;typeof r!="function"&&t&&t[r]===n&&(r=null);var o=-1,i=n?n.length:0;if(null==r&&typeof i=="number")for(;++o<i;)t=n[o],t<u&&(u=t);else r=X(r,t,3),D(n,function(n,t,o){t=r(n,t,o),t<e&&(e=t,u=n)});return u},u.omit=function(n){var r=[];Kr(n,function(n,t){r.push(t)});for(var r=c(r,p(arguments,true,false,1)),t=-1,e=r.length,u={};++t<e;){var o=r[t];u[o]=n[o]}return u},u.once=function(n){var r,t;if(!A(n))throw new TypeError;return function(){return r?t:(r=true,t=n.apply(this,arguments),n=null,t)
+}},u.pairs=function(n){for(var r=-1,t=Ur(n),e=t.length,u=Array(e);++r<e;){var o=t[r];u[r]=[o,n[o]]}return u},u.partial=function(n){return v(n,16,e(arguments,1))},u.pick=function(n){for(var r=-1,t=p(arguments,true,false,1),e=t.length,u={};++r<e;){var o=t[r];o in n&&(u[o]=n[o])}return u},u.pluck=Zr,u.range=function(n,r,t){n=+n||0,t=+t||1,null==r&&(r=n,n=0);var e=-1;r=Mr(0,Or((r-n)/t));for(var u=Array(r);++e<r;)u[e]=n,n+=t;return u},u.reject=function(n,r,t){return r=X(r,t,3),F(n,function(n,t,e){return!r(n,t,e)
+})},u.rest=H,u.shuffle=C,u.sortBy=function(n,t,e){var u=-1,o=n?n.length:0,i=Array(typeof o=="number"?o:0);for(t=X(t,e,3),D(n,function(n,r,e){i[++u]={m:[t(n,r,e)],n:u,o:n}}),o=i.length,i.sort(r);o--;)i[o]=i[o].o;return i},u.tap=function(n,r){return r(n),n},u.throttle=function(n,r,t){var e=true,u=true;if(!A(n))throw new TypeError;return false===t?e=false:O(t)&&(e="leading"in t?t.leading:e,u="trailing"in t?t.trailing:u),t={},t.leading=e,t.maxWait=r,t.trailing=u,Q(n,r,t)},u.times=function(n,r,t){n=-1<(n=+n)?n:0;
+var e=-1,u=Array(n);for(r=a(r,t,1);++e<n;)u[e]=r(e);return u},u.toArray=function(n){return Cr(n)?e(n):n&&typeof n.length=="number"?M(n):R(n)},u.union=function(){return g(p(arguments,true,true))},u.uniq=K,u.values=R,u.where=U,u.without=function(n){return c(n,e(arguments,1))},u.wrap=function(n,r){return v(r,16,[n])},u.zip=function(){for(var n=-1,r=$(Zr(arguments,"length")),t=Array(0>r?0:r);++n<r;)t[n]=Zr(arguments,n);return t},u.collect=M,u.drop=H,u.each=D,u.extend=w,u.methods=x,u.object=function(n,r){var t=-1,e=n?n.length:0,u={};
+for(r||!e||Cr(n[0])||(r=[]);++t<e;){var o=n[t];r?u[o]=r[t]:o&&(u[o[0]]=o[1])}return u},u.select=F,u.tail=H,u.unique=K,u.clone=function(n){return O(n)?Cr(n)?e(n):w({},n):n},u.contains=k,u.escape=function(n){return null==n?"":(n+"").replace(Jr,y)},u.every=B,u.find=q,u.has=function(n,r){return n?Nr.call(n,r):false},u.identity=Y,u.indexOf=G,u.isArguments=b,u.isArray=Cr,u.isBoolean=function(n){return true===n||false===n||n&&typeof n=="object"&&Er.call(n)==lr||false},u.isDate=function(n){return n&&typeof n=="object"&&Er.call(n)==cr||false
+},u.isElement=function(n){return n&&1===n.nodeType||false},u.isEmpty=E,u.isEqual=function(n,r){return s(n,r)},u.isFinite=function(n){return qr(n)&&!Dr(parseFloat(n))},u.isFunction=A,u.isNaN=function(n){return S(n)&&n!=+n},u.isNull=function(n){return null===n},u.isNumber=S,u.isObject=O,u.isRegExp=function(n){return n&&vr[typeof n]&&Er.call(n)==gr||false},u.isString=N,u.isUndefined=function(n){return typeof n=="undefined"},u.lastIndexOf=function(n,r,t){var e=n?n.length:0;for(typeof t=="number"&&(e=(0>t?Mr(0,e+t):$r(t,e-1))+1);e--;)if(n[e]===r)return e;
+return-1},u.mixin=Z,u.noConflict=function(){return mr._=Tr,this},u.random=function(n,r){return null==n&&null==r&&(r=1),n=+n||0,null==r?(r=n,n=0):r=+r||0,n+Sr(Wr()*(r-n+1))},u.reduce=W,u.reduceRight=z,u.result=function(n,r){if(n){var t=n[r];return A(t)?n[r]():t}},u.size=function(n){var r=n?n.length:0;return typeof r=="number"?r:Ur(n).length},u.some=P,u.sortedIndex=J,u.template=function(n,r,e){var o=u,i=o.templateSettings;n=(n||"")+"",e=j({},e,i);var f=0,a="__p+='",i=e.variable;n.replace(RegExp((e.escape||or).source+"|"+(e.interpolate||or).source+"|"+(e.evaluate||or).source+"|$","g"),function(r,e,u,o,i){return a+=n.slice(f,i).replace(ir,t),e&&(a+="'+_.escape("+e+")+'"),o&&(a+="';"+o+";\n__p+='"),u&&(a+="'+((__t=("+u+"))==null?'':__t)+'"),f=i+r.length,r
+}),a+="';",i||(i="obj",a="with("+i+"||{}){"+a+"}"),a="function("+i+"){var __t,__p='',__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}"+a+"return __p}";try{var l=Function("_","return "+a)(o)}catch(c){throw c.source=a,c}return r?l(r):(l.source=a,l)},u.unescape=function(n){return null==n?"":(n+"").replace(Hr,d)},u.uniqueId=function(n){var r=++tr+"";return n?n+r:r},u.all=B,u.any=P,u.detect=q,u.findWhere=function(n,r){return U(n,r,true)},u.foldl=W,u.foldr=z,u.include=k,u.inject=W,u.first=V,u.last=function(n,r,t){var u=0,o=n?n.length:0;
+if(typeof r!="number"&&null!=r){var i=o;for(r=X(r,t,3);i--&&r(n[i],i,n);)u++}else if(u=r,null==u||t)return n?n[o-1]:rr;return e(n,Mr(0,o-u))},u.sample=function(n,r,t){return n&&typeof n.length!="number"&&(n=R(n)),null==r||t?n?n[0+Sr(Wr()*(n.length-1-0+1))]:rr:(n=C(n),n.length=$r(Mr(0,r),n.length),n)},u.take=V,u.head=V,Z(u),u.VERSION="2.4.2",u.prototype.chain=function(){return this.__chain__=true,this},u.prototype.value=function(){return this.__wrapped__},D("pop push reverse shift sort splice unshift".split(" "),function(n){var r=jr[n];
+u.prototype[n]=function(){var n=this.__wrapped__;return r.apply(n,arguments),zr.spliceObjects||0!==n.length||delete n[0],this}}),D(["concat","join","slice"],function(n){var r=jr[n];u.prototype[n]=function(){var n=r.apply(this.__wrapped__,arguments);return this.__chain__&&(n=new o(n),n.__chain__=true),n}}),typeof define=="function"&&typeof define.amd=="object"&&define.amd?(mr._=u, define(function(){return u})):_r&&dr?br?(dr.exports=u)._=u:_r._=u:mr._=u}).call(this);
\ No newline at end of file
--- /dev/null
+/**
+ * @license
+ * Lo-Dash 2.4.2 <https://lodash.com/>
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <https://lodash.com/license>
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre ES5 environments */
+ var undefined;
+
+ /** Used to pool arrays and objects used internally */
+ var arrayPool = [],
+ objectPool = [];
+
+ /** Used to generate unique IDs */
+ var idCounter = 0;
+
+ /** Used internally to indicate various things */
+ var indicatorObject = {};
+
+ /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+ var keyPrefix = +new Date + '';
+
+ /** Used as the size when optimizations are enabled for large arrays */
+ var largeArraySize = 75;
+
+ /** Used as the max size of the `arrayPool` and `objectPool` */
+ var maxPoolSize = 40;
+
+ /** Used to detect and test whitespace */
+ var whitespace = (
+ // whitespace
+ ' \t\x0B\f\xA0\ufeff' +
+
+ // line terminators
+ '\n\r\u2028\u2029' +
+
+ // unicode category "Zs" space separators
+ '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+ );
+
+ /** Used to match empty string literals in compiled template source */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+ /**
+ * Used to match ES6 template delimiters
+ * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+ /** Used to match regexp flags from their coerced string values */
+ var reFlags = /\w*$/;
+
+ /** Used to detected named functions */
+ var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+ /** Used to match "interpolate" template delimiters */
+ var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+ /** Used to match leading whitespace and zeros to be removed */
+ var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+ /** Used to ensure capturing order of template delimiters */
+ var reNoMatch = /($^)/;
+
+ /** Used to detect functions containing a `this` reference */
+ var reThis = /\bthis\b/;
+
+ /** Used to match unescaped characters in compiled string literals */
+ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+ /** Used to assign default `context` object properties */
+ var contextProps = [
+ 'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object',
+ 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+ 'parseInt', 'setTimeout'
+ ];
+
+ /** Used to fix the JScript [[DontEnum]] bug */
+ var shadowedProps = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+ ];
+
+ /** Used to make template sourceURLs easier to identify */
+ var templateCounter = 0;
+
+ /** `Object#toString` result shortcuts */
+ var argsClass = '[object Arguments]',
+ arrayClass = '[object Array]',
+ boolClass = '[object Boolean]',
+ dateClass = '[object Date]',
+ errorClass = '[object Error]',
+ funcClass = '[object Function]',
+ numberClass = '[object Number]',
+ objectClass = '[object Object]',
+ regexpClass = '[object RegExp]',
+ stringClass = '[object String]';
+
+ /** Used to identify object classifications that `_.clone` supports */
+ var cloneableClasses = {};
+ cloneableClasses[funcClass] = false;
+ cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+ cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+ cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+ cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+ /** Used as an internal `_.debounce` options object */
+ var debounceOptions = {
+ 'leading': false,
+ 'maxWait': 0,
+ 'trailing': false
+ };
+
+ /** Used as the property descriptor for `__bindData__` */
+ var descriptor = {
+ 'configurable': false,
+ 'enumerable': false,
+ 'value': null,
+ 'writable': false
+ };
+
+ /** Used as the data object for `iteratorTemplate` */
+ var iteratorData = {
+ 'args': '',
+ 'array': null,
+ 'bottom': '',
+ 'firstArg': '',
+ 'init': '',
+ 'keys': null,
+ 'loop': '',
+ 'shadowedProps': null,
+ 'support': null,
+ 'top': '',
+ 'useHas': false
+ };
+
+ /** Used to determine if values are of the language type Object */
+ var objectTypes = {
+ 'boolean': false,
+ 'function': true,
+ 'object': true,
+ 'number': false,
+ 'string': false,
+ 'undefined': false
+ };
+
+ /** Used to escape characters for inclusion in compiled string literals */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ /** Used as a reference to the global object */
+ var root = (objectTypes[typeof window] && window) || this;
+
+ /** Detect free variable `exports` */
+ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+ /** Detect free variable `module` */
+ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+ /** Detect the popular CommonJS extension `module.exports` */
+ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+ /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+ var freeGlobal = objectTypes[typeof global] && global;
+ if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+ root = freeGlobal;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.indexOf` without support for binary searches
+ * or `fromIndex` constraints.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * An implementation of `_.contains` for cache objects that mimics the return
+ * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+ *
+ * @private
+ * @param {Object} cache The cache object to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `0` if `value` is found, else `-1`.
+ */
+ function cacheIndexOf(cache, value) {
+ var type = typeof value;
+ cache = cache.cache;
+
+ if (type == 'boolean' || value == null) {
+ return cache[value] ? 0 : -1;
+ }
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value;
+ cache = (cache = cache[type]) && cache[key];
+
+ return type == 'object'
+ ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+ : (cache ? 0 : -1);
+ }
+
+ /**
+ * Adds a given value to the corresponding cache object.
+ *
+ * @private
+ * @param {*} value The value to add to the cache.
+ */
+ function cachePush(value) {
+ var cache = this.cache,
+ type = typeof value;
+
+ if (type == 'boolean' || value == null) {
+ cache[value] = true;
+ } else {
+ if (type != 'number' && type != 'string') {
+ type = 'object';
+ }
+ var key = type == 'number' ? value : keyPrefix + value,
+ typeCache = cache[type] || (cache[type] = {});
+
+ if (type == 'object') {
+ (typeCache[key] || (typeCache[key] = [])).push(value);
+ } else {
+ typeCache[key] = true;
+ }
+ }
+ }
+
+ /**
+ * Used by `_.max` and `_.min` as the default callback when a given
+ * collection is a string value.
+ *
+ * @private
+ * @param {string} value The character to inspect.
+ * @returns {number} Returns the code unit of given character.
+ */
+ function charAtCallback(value) {
+ return value.charCodeAt(0);
+ }
+
+ /**
+ * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+ * them in ascending order.
+ *
+ * @private
+ * @param {Object} a The object to compare to `b`.
+ * @param {Object} b The object to compare to `a`.
+ * @returns {number} Returns the sort order indicator of `1` or `-1`.
+ */
+ function compareAscending(a, b) {
+ var ac = a.criteria,
+ bc = b.criteria,
+ index = -1,
+ length = ac.length;
+
+ while (++index < length) {
+ var value = ac[index],
+ other = bc[index];
+
+ if (value !== other) {
+ if (value > other || typeof value == 'undefined') {
+ return 1;
+ }
+ if (value < other || typeof other == 'undefined') {
+ return -1;
+ }
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to return the same value for
+ // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See http://code.google.com/p/v8/issues/detail?id=90
+ return a.index - b.index;
+ }
+
+ /**
+ * Creates a cache object to optimize linear searches of large arrays.
+ *
+ * @private
+ * @param {Array} [array=[]] The array to search.
+ * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+ */
+ function createCache(array) {
+ var index = -1,
+ length = array.length,
+ first = array[0],
+ mid = array[(length / 2) | 0],
+ last = array[length - 1];
+
+ if (first && typeof first == 'object' &&
+ mid && typeof mid == 'object' && last && typeof last == 'object') {
+ return false;
+ }
+ var cache = getObject();
+ cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+ var result = getObject();
+ result.array = array;
+ result.cache = cache;
+ result.push = cachePush;
+
+ while (++index < length) {
+ result.push(array[index]);
+ }
+ return result;
+ }
+
+ /**
+ * Used by `template` to escape characters for inclusion in compiled
+ * string literals.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(match) {
+ return '\\' + stringEscapes[match];
+ }
+
+ /**
+ * Gets an array from the array pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Array} The array from the pool.
+ */
+ function getArray() {
+ return arrayPool.pop() || [];
+ }
+
+ /**
+ * Gets an object from the object pool or creates a new one if the pool is empty.
+ *
+ * @private
+ * @returns {Object} The object from the pool.
+ */
+ function getObject() {
+ return objectPool.pop() || {
+ 'array': null,
+ 'cache': null,
+ 'criteria': null,
+ 'false': false,
+ 'index': 0,
+ 'null': false,
+ 'number': null,
+ 'object': null,
+ 'push': null,
+ 'string': null,
+ 'true': false,
+ 'undefined': false,
+ 'value': null
+ };
+ }
+
+ /**
+ * Checks if `value` is a DOM node in IE < 9.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`.
+ */
+ function isNode(value) {
+ // IE < 9 presents DOM nodes as `Object` objects except they have `toString`
+ // methods that are `typeof` "string" and still can coerce nodes to strings
+ return typeof value.toString != 'function' && typeof (value + '') == 'string';
+ }
+
+ /**
+ * Releases the given array back to the array pool.
+ *
+ * @private
+ * @param {Array} [array] The array to release.
+ */
+ function releaseArray(array) {
+ array.length = 0;
+ if (arrayPool.length < maxPoolSize) {
+ arrayPool.push(array);
+ }
+ }
+
+ /**
+ * Releases the given object back to the object pool.
+ *
+ * @private
+ * @param {Object} [object] The object to release.
+ */
+ function releaseObject(object) {
+ var cache = object.cache;
+ if (cache) {
+ releaseObject(cache);
+ }
+ object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+ if (objectPool.length < maxPoolSize) {
+ objectPool.push(object);
+ }
+ }
+
+ /**
+ * Slices the `collection` from the `start` index up to, but not including,
+ * the `end` index.
+ *
+ * Note: This function is used instead of `Array#slice` to support node lists
+ * in IE < 9 and to ensure dense arrays are returned.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to slice.
+ * @param {number} start The start index.
+ * @param {number} end The end index.
+ * @returns {Array} Returns the new array.
+ */
+ function slice(array, start, end) {
+ start || (start = 0);
+ if (typeof end == 'undefined') {
+ end = array ? array.length : 0;
+ }
+ var index = -1,
+ length = end - start || 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = array[start + index];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Create a new `lodash` function using the given context object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} [context=root] The context object.
+ * @returns {Function} Returns the `lodash` function.
+ */
+ function runInContext(context) {
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See http://es5.github.io/#x11.1.5.
+ context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+ /** Native constructor references */
+ var Array = context.Array,
+ Boolean = context.Boolean,
+ Date = context.Date,
+ Error = context.Error,
+ Function = context.Function,
+ Math = context.Math,
+ Number = context.Number,
+ Object = context.Object,
+ RegExp = context.RegExp,
+ String = context.String,
+ TypeError = context.TypeError;
+
+ /**
+ * Used for `Array` method references.
+ *
+ * Normally `Array.prototype` would suffice, however, using an array literal
+ * avoids issues in Narwhal.
+ */
+ var arrayRef = [];
+
+ /** Used for native method references */
+ var errorProto = Error.prototype,
+ objectProto = Object.prototype,
+ stringProto = String.prototype;
+
+ /** Used to restore the original `_` reference in `noConflict` */
+ var oldDash = context._;
+
+ /** Used to resolve the internal [[Class]] of values */
+ var toString = objectProto.toString;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(toString)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/toString| for [^\]]+/g, '.*?') + '$'
+ );
+
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ clearTimeout = context.clearTimeout,
+ floor = Math.floor,
+ fnToString = Function.prototype.toString,
+ getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+ hasOwnProperty = objectProto.hasOwnProperty,
+ push = arrayRef.push,
+ propertyIsEnumerable = objectProto.propertyIsEnumerable,
+ setTimeout = context.setTimeout,
+ splice = arrayRef.splice,
+ unshift = arrayRef.unshift;
+
+ /** Used to set meta data on functions */
+ var defineProperty = (function() {
+ // IE 8 only accepts DOM elements
+ try {
+ var o = {},
+ func = isNative(func = Object.defineProperty) && func,
+ result = func(o, o, o) && func;
+ } catch(e) { }
+ return result;
+ }());
+
+ /* Native method shortcuts for methods with the same name as other `lodash` methods */
+ var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+ nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+ nativeIsFinite = context.isFinite,
+ nativeIsNaN = context.isNaN,
+ nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeParseInt = context.parseInt,
+ nativeRandom = Math.random;
+
+ /** Used to lookup a built-in constructor by [[Class]] */
+ var ctorByClass = {};
+ ctorByClass[arrayClass] = Array;
+ ctorByClass[boolClass] = Boolean;
+ ctorByClass[dateClass] = Date;
+ ctorByClass[funcClass] = Function;
+ ctorByClass[objectClass] = Object;
+ ctorByClass[numberClass] = Number;
+ ctorByClass[regexpClass] = RegExp;
+ ctorByClass[stringClass] = String;
+
+ /** Used to avoid iterating non-enumerable properties in IE < 9 */
+ var nonEnumProps = {};
+ nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
+ nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
+ nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true };
+ nonEnumProps[objectClass] = { 'constructor': true };
+
+ (function() {
+ var length = shadowedProps.length;
+ while (length--) {
+ var key = shadowedProps[length];
+ for (var className in nonEnumProps) {
+ if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) {
+ nonEnumProps[className][key] = false;
+ }
+ }
+ }
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps the given value to enable intuitive
+ * method chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+ * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+ * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+ * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+ * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+ * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * and `zip`
+ *
+ * The non-chainable wrapper functions are:
+ * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+ * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+ * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+ * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+ * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+ * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+ * `template`, `unescape`, `uniqueId`, and `value`
+ *
+ * The wrapper functions `first` and `last` return wrapped values when `n` is
+ * provided, otherwise they return unwrapped values.
+ *
+ * Explicit chaining can be enabled by using the `_.chain` method.
+ *
+ * @name _
+ * @constructor
+ * @category Chaining
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns a `lodash` instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(num) {
+ * return num * num;
+ * });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+ return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+ ? value
+ : new lodashWrapper(value);
+ }
+
+ /**
+ * A fast path for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @param {boolean} chainAll A flag to enable chaining for all methods
+ * @returns {Object} Returns a `lodash` instance.
+ */
+ function lodashWrapper(value, chainAll) {
+ this.__chain__ = !!chainAll;
+ this.__wrapped__ = value;
+ }
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
+
+ /**
+ * An object used to flag environments features.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ var support = lodash.support = {};
+
+ (function() {
+ var ctor = function() { this.x = 1; },
+ object = { '0': 1, 'length': 1 },
+ props = [];
+
+ ctor.prototype = { 'valueOf': 1, 'y': 1 };
+ for (var key in new ctor) { props.push(key); }
+ for (key in arguments) { }
+
+ /**
+ * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.argsClass = toString.call(arguments) == argsClass;
+
+ /**
+ * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.argsObject = arguments.constructor == Object && !(arguments instanceof Array);
+
+ /**
+ * Detect if `name` or `message` properties of `Error.prototype` are
+ * enumerable by default. (IE < 9, Safari < 5.1)
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name');
+
+ /**
+ * Detect if `prototype` properties are enumerable by default.
+ *
+ * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
+ * (if the prototype or a property on the prototype has been set)
+ * incorrectly sets a function's `prototype` property [[Enumerable]]
+ * value to `true`.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype');
+
+ /**
+ * Detect if functions can be decompiled by `Function#toString`
+ * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+ /**
+ * Detect if `Function#name` is supported (all but IE).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcNames = typeof Function.name == 'string';
+
+ /**
+ * Detect if `arguments` object indexes are non-enumerable
+ * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nonEnumArgs = key != 0;
+
+ /**
+ * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
+ *
+ * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
+ * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nonEnumShadows = !/valueOf/.test(props);
+
+ /**
+ * Detect if own properties are iterated after inherited properties (all but IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.ownLast = props[0] != 'x';
+
+ /**
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
+ *
+ * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
+ * and `splice()` functions that fail to remove the last element, `value[0]`,
+ * of array-like objects even though the `length` property is set to `0`.
+ * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
+ * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
+
+ /**
+ * Detect lack of support for accessing string characters by index.
+ *
+ * IE < 8 can't access characters by index and IE 8 can only access
+ * characters by index on string literals.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
+
+ /**
+ * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9)
+ * and that the JS engine errors when attempting to coerce an object to
+ * a string without a `toString` function.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + ''));
+ } catch(e) {
+ support.nodeClass = true;
+ }
+ }(1));
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in
+ * embedded Ruby (ERB). Change the following template settings to use alternative
+ * delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': /<%-([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': /<%([\s\S]+?)%>/g,
+
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
+
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type string
+ */
+ 'variable': '',
+
+ /**
+ * Used to import variables into the compiled template.
+ *
+ * @memberOf _.templateSettings
+ * @type Object
+ */
+ 'imports': {
+
+ /**
+ * A reference to the `lodash` function.
+ *
+ * @memberOf _.templateSettings.imports
+ * @type Function
+ */
+ '_': lodash
+ }
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The template used to create iterator functions.
+ *
+ * @private
+ * @param {Object} data The data object used to populate the text.
+ * @returns {string} Returns the interpolated text.
+ */
+ var iteratorTemplate = template(
+ // the `iterable` may be reassigned by the `top` snippet
+ 'var index, iterable = <%= firstArg %>, ' +
+ // assign the `result` variable an initial value
+ 'result = <%= init %>;\n' +
+ // exit early if the first argument is falsey
+ 'if (!iterable) return result;\n' +
+ // add code before the iteration branches
+ '<%= top %>;' +
+
+ // array-like iteration:
+ '<% if (array) { %>\n' +
+ 'var length = iterable.length; index = -1;\n' +
+ 'if (<%= array %>) {' +
+
+ // add support for accessing string characters by index if needed
+ ' <% if (support.unindexedChars) { %>\n' +
+ ' if (isString(iterable)) {\n' +
+ " iterable = iterable.split('')\n" +
+ ' }' +
+ ' <% } %>\n' +
+
+ // iterate over the array-like value
+ ' while (++index < length) {\n' +
+ ' <%= loop %>;\n' +
+ ' }\n' +
+ '}\n' +
+ 'else {' +
+
+ // object iteration:
+ // add support for iterating over `arguments` objects if needed
+ ' <% } else if (support.nonEnumArgs) { %>\n' +
+ ' var length = iterable.length; index = -1;\n' +
+ ' if (length && isArguments(iterable)) {\n' +
+ ' while (++index < length) {\n' +
+ " index += '';\n" +
+ ' <%= loop %>;\n' +
+ ' }\n' +
+ ' } else {' +
+ ' <% } %>' +
+
+ // avoid iterating over `prototype` properties in older Firefox, Opera, and Safari
+ ' <% if (support.enumPrototypes) { %>\n' +
+ " var skipProto = typeof iterable == 'function';\n" +
+ ' <% } %>' +
+
+ // avoid iterating over `Error.prototype` properties in older IE and Safari
+ ' <% if (support.enumErrorProps) { %>\n' +
+ ' var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n' +
+ ' <% } %>' +
+
+ // define conditions used in the loop
+ ' <%' +
+ ' var conditions = [];' +
+ ' if (support.enumPrototypes) { conditions.push(\'!(skipProto && index == "prototype")\'); }' +
+ ' if (support.enumErrorProps) { conditions.push(\'!(skipErrorProps && (index == "message" || index == "name"))\'); }' +
+ ' %>' +
+
+ // iterate own properties using `Object.keys`
+ ' <% if (useHas && keys) { %>\n' +
+ ' var ownIndex = -1,\n' +
+ ' ownProps = objectTypes[typeof iterable] && keys(iterable),\n' +
+ ' length = ownProps ? ownProps.length : 0;\n\n' +
+ ' while (++ownIndex < length) {\n' +
+ ' index = ownProps[ownIndex];\n<%' +
+ " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" +
+ ' <%= loop %>;' +
+ ' <% if (conditions.length) { %>\n }<% } %>\n' +
+ ' }' +
+
+ // else using a for-in loop
+ ' <% } else { %>\n' +
+ ' for (index in iterable) {\n<%' +
+ ' if (useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); }' +
+ " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" +
+ ' <%= loop %>;' +
+ ' <% if (conditions.length) { %>\n }<% } %>\n' +
+ ' }' +
+
+ // Because IE < 9 can't set the `[[Enumerable]]` attribute of an
+ // existing property and the `constructor` property of a prototype
+ // defaults to non-enumerable, Lo-Dash skips the `constructor`
+ // property when it infers it's iterating over a `prototype` object.
+ ' <% if (support.nonEnumShadows) { %>\n\n' +
+ ' if (iterable !== objectProto) {\n' +
+ " var ctor = iterable.constructor,\n" +
+ ' isProto = iterable === (ctor && ctor.prototype),\n' +
+ ' className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n' +
+ ' nonEnum = nonEnumProps[className];\n' +
+ ' <% for (k = 0; k < 7; k++) { %>\n' +
+ " index = '<%= shadowedProps[k] %>';\n" +
+ ' if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))<%' +
+ ' if (!useHas) { %> || (!nonEnum[index] && iterable[index] !== objectProto[index])<% }' +
+ ' %>) {\n' +
+ ' <%= loop %>;\n' +
+ ' }' +
+ ' <% } %>\n' +
+ ' }' +
+ ' <% } %>' +
+ ' <% } %>' +
+ ' <% if (array || support.nonEnumArgs) { %>\n}<% } %>\n' +
+
+ // add code to the bottom of the iteration function
+ '<%= bottom %>;\n' +
+ // finally, return the `result`
+ 'return result'
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The base implementation of `_.bind` that creates the bound function and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new bound function.
+ */
+ function baseBind(bindData) {
+ var func = bindData[0],
+ partialArgs = bindData[2],
+ thisArg = bindData[4];
+
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.io/#x15.3.4.5
+ if (partialArgs) {
+ // avoid `arguments` object deoptimizations by using `slice` instead
+ // of `Array.prototype.slice.call` and not assigning `arguments` to a
+ // variable as a ternary expression
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ // mimic the constructor's `return` behavior
+ // http://es5.github.io/#x13.2.2
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ var thisBinding = baseCreate(func.prototype),
+ result = func.apply(thisBinding, args || arguments);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisArg, args || arguments);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.clone` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+ function baseClone(value, isDeep, callback, stackA, stackB) {
+ if (callback) {
+ var result = callback(value);
+ if (typeof result != 'undefined') {
+ return result;
+ }
+ }
+ // inspect [[Class]]
+ var isObj = isObject(value);
+ if (isObj) {
+ var className = toString.call(value);
+ if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) {
+ return value;
+ }
+ var ctor = ctorByClass[className];
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return new ctor(+value);
+
+ case numberClass:
+ case stringClass:
+ return new ctor(value);
+
+ case regexpClass:
+ result = ctor(value.source, reFlags.exec(value));
+ result.lastIndex = value.lastIndex;
+ return result;
+ }
+ } else {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isDeep) {
+ // check for circular references and return corresponding clone
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == value) {
+ return stackB[length];
+ }
+ }
+ result = isArr ? ctor(value.length) : {};
+ }
+ else {
+ result = isArr ? slice(value) : assign({}, value);
+ }
+ // add array properties assigned by `RegExp#exec`
+ if (isArr) {
+ if (hasOwnProperty.call(value, 'index')) {
+ result.index = value.index;
+ }
+ if (hasOwnProperty.call(value, 'input')) {
+ result.input = value.input;
+ }
+ }
+ // exit for shallow clone
+ if (!isDeep) {
+ return result;
+ }
+ // add the source value to the stack of traversed objects
+ // and associate it with its clone
+ stackA.push(value);
+ stackB.push(result);
+
+ // recursively populate clone (susceptible to call stack limits)
+ (isArr ? baseEach : forOwn)(value, function(objValue, key) {
+ result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+ });
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ function baseCreate(prototype, properties) {
+ return isObject(prototype) ? nativeCreate(prototype) : {};
+ }
+ // fallback for browsers without `Object.create`
+ if (!nativeCreate) {
+ baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || context.Object();
+ };
+ }());
+ }
+
+ /**
+ * The base implementation of `_.createCallback` without support for creating
+ * "_.pluck" or "_.where" style callbacks.
+ *
+ * @private
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ */
+ function baseCreateCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ // exit early for no `thisArg` or already bound by `Function#bind`
+ if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+ return func;
+ }
+ var bindData = func.__bindData__;
+ if (typeof bindData == 'undefined') {
+ if (support.funcNames) {
+ bindData = !func.name;
+ }
+ bindData = bindData || !support.funcDecomp;
+ if (!bindData) {
+ var source = fnToString.call(func);
+ if (!support.funcNames) {
+ bindData = !reFuncName.test(source);
+ }
+ if (!bindData) {
+ // checks if `func` references the `this` keyword and stores the result
+ bindData = reThis.test(source);
+ setBindData(func, bindData);
+ }
+ }
+ }
+ // exit early if there are no `this` references or `func` is bound
+ if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 2: return function(a, b) {
+ return func.call(thisArg, a, b);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ }
+ return bind(func, thisArg);
+ }
+
+ /**
+ * The base implementation of `createWrapper` that creates the wrapper and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new function.
+ */
+ function baseCreateWrapper(bindData) {
+ var func = bindData[0],
+ bitmask = bindData[1],
+ partialArgs = bindData[2],
+ partialRightArgs = bindData[3],
+ thisArg = bindData[4],
+ arity = bindData[5];
+
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ key = func;
+
+ function bound() {
+ var thisBinding = isBind ? thisArg : this;
+ if (partialArgs) {
+ var args = slice(partialArgs);
+ push.apply(args, arguments);
+ }
+ if (partialRightArgs || isCurry) {
+ args || (args = slice(arguments));
+ if (partialRightArgs) {
+ push.apply(args, partialRightArgs);
+ }
+ if (isCurry && args.length < arity) {
+ bitmask |= 16 & ~32;
+ return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+ }
+ }
+ args || (args = arguments);
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (this instanceof bound) {
+ thisBinding = baseCreate(func.prototype);
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.difference` that accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {Array} [values] The array of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+ result = [];
+
+ if (isLarge) {
+ var cache = createCache(values);
+ if (cache) {
+ indexOf = cacheIndexOf;
+ values = cache;
+ } else {
+ isLarge = false;
+ }
+ }
+ while (++index < length) {
+ var value = array[index];
+ if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseObject(values);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` without support for callback
+ * shorthands or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+ * @param {number} [fromIndex=0] The index to start from.
+ * @returns {Array} Returns a new flattened array.
+ */
+ function baseFlatten(array, isShallow, isStrict, fromIndex) {
+ var index = (fromIndex || 0) - 1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+
+ if (value && typeof value == 'object' && typeof value.length == 'number'
+ && (isArray(value) || isArguments(value))) {
+ // recursively flatten arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ value = baseFlatten(value, isShallow, isStrict);
+ }
+ var valIndex = -1,
+ valLength = value.length,
+ resIndex = result.length;
+
+ result.length += valLength;
+ while (++valIndex < valLength) {
+ result[resIndex++] = value[valIndex];
+ }
+ } else if (!isStrict) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+ * that allows partial "_.where" style comparisons.
+ *
+ * @private
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+ // used to indicate that when comparing objects, `a` has at least the properties of `b`
+ if (callback) {
+ var result = callback(a, b);
+ if (typeof result != 'undefined') {
+ return !!result;
+ }
+ }
+ // exit early for identical values
+ if (a === b) {
+ // treat `+0` vs. `-0` as not equal
+ return a !== 0 || (1 / a == 1 / b);
+ }
+ var type = typeof a,
+ otherType = typeof b;
+
+ // exit early for unlike primitive values
+ if (a === a &&
+ !(a && objectTypes[type]) &&
+ !(b && objectTypes[otherType])) {
+ return false;
+ }
+ // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+ // http://es5.github.io/#x15.3.4.4
+ if (a == null || b == null) {
+ return a === b;
+ }
+ // compare [[Class]] names
+ var className = toString.call(a),
+ otherClass = toString.call(b);
+
+ if (className == argsClass) {
+ className = objectClass;
+ }
+ if (otherClass == argsClass) {
+ otherClass = objectClass;
+ }
+ if (className != otherClass) {
+ return false;
+ }
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ // coerce dates and booleans to numbers, dates to milliseconds and booleans
+ // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+ return +a == +b;
+
+ case numberClass:
+ // treat `NaN` vs. `NaN` as equal
+ return (a != +a)
+ ? b != +b
+ // but treat `+0` vs. `-0` as not equal
+ : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+ case regexpClass:
+ case stringClass:
+ // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+ // treat string primitives and their corresponding object instances as equal
+ return a == String(b);
+ }
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ // unwrap any `lodash` wrapped values
+ var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+ bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+ if (aWrapped || bWrapped) {
+ return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+ }
+ // exit for functions and DOM nodes
+ if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) {
+ return false;
+ }
+ // in older versions of Opera, `arguments` objects have `Array` constructors
+ var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor,
+ ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor;
+
+ // non `Object` object instances with different constructors are not equal
+ if (ctorA != ctorB &&
+ !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+ ('constructor' in a && 'constructor' in b)
+ ) {
+ return false;
+ }
+ }
+ // assume cyclic structures are equal
+ // the algorithm for detecting cyclic structures is adapted from ES 5.1
+ // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+ var initedStack = !stackA;
+ stackA || (stackA = getArray());
+ stackB || (stackB = getArray());
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
+ }
+ }
+ var size = 0;
+ result = true;
+
+ // add `a` and `b` to the stack of traversed objects
+ stackA.push(a);
+ stackB.push(b);
+
+ // recursively compare objects and arrays (susceptible to call stack limits)
+ if (isArr) {
+ // compare lengths to determine if a deep comparison is necessary
+ length = a.length;
+ size = b.length;
+ result = size == length;
+
+ if (result || isWhere) {
+ // deep compare the contents, ignoring non-numeric properties
+ while (size--) {
+ var index = length,
+ value = b[size];
+
+ if (isWhere) {
+ while (index--) {
+ if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+ // which, in this case, is more costly
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ // count the number of properties.
+ size++;
+ // deep compare each property value.
+ return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+ }
+ });
+
+ if (result && !isWhere) {
+ // ensure both objects have the same number of properties
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ // `size` will be `-1` if `a` has more properties than `b`
+ return (result = --size > -1);
+ }
+ });
+ }
+ }
+ stackA.pop();
+ stackB.pop();
+
+ if (initedStack) {
+ releaseArray(stackA);
+ releaseArray(stackB);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.merge` without argument juggling or support
+ * for `thisArg` binding.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates values with source counterparts.
+ */
+ function baseMerge(object, source, callback, stackA, stackB) {
+ (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+ var found,
+ isArr,
+ result = source,
+ value = object[key];
+
+ if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+ // avoid merging previously merged cyclic sources
+ var stackLength = stackA.length;
+ while (stackLength--) {
+ if ((found = stackA[stackLength] == source)) {
+ value = stackB[stackLength];
+ break;
+ }
+ }
+ if (!found) {
+ var isShallow;
+ if (callback) {
+ result = callback(value, source);
+ if ((isShallow = typeof result != 'undefined')) {
+ value = result;
+ }
+ }
+ if (!isShallow) {
+ value = isArr
+ ? (isArray(value) ? value : [])
+ : (isPlainObject(value) ? value : {});
+ }
+ // add `source` and associated `value` to the stack of traversed objects
+ stackA.push(source);
+ stackB.push(value);
+
+ // recursively merge objects and arrays (susceptible to call stack limits)
+ if (!isShallow) {
+ baseMerge(value, source, callback, stackA, stackB);
+ }
+ }
+ }
+ else {
+ if (callback) {
+ result = callback(value, source);
+ if (typeof result == 'undefined') {
+ result = source;
+ }
+ }
+ if (typeof result != 'undefined') {
+ value = result;
+ }
+ }
+ object[key] = value;
+ });
+ }
+
+ /**
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
+ /**
+ * The base implementation of `_.uniq` without support for callback shorthands
+ * or `thisArg` binding.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function} [callback] The function called per iteration.
+ * @returns {Array} Returns a duplicate-value-free array.
+ */
+ function baseUniq(array, isSorted, callback) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ result = [];
+
+ var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+ seen = (callback || isLarge) ? getArray() : result;
+
+ if (isLarge) {
+ var cache = createCache(seen);
+ indexOf = cacheIndexOf;
+ seen = cache;
+ }
+ while (++index < length) {
+ var value = array[index],
+ computed = callback ? callback(value, index, array) : value;
+
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : indexOf(seen, computed) < 0
+ ) {
+ if (callback || isLarge) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseArray(seen.array);
+ releaseObject(seen);
+ } else if (callback) {
+ releaseArray(seen);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a function that aggregates a collection, creating an object composed
+ * of keys generated from the results of running each element of the collection
+ * through a callback. The given `setter` function sets the keys and values
+ * of the composed object.
+ *
+ * @private
+ * @param {Function} setter The setter function.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter) {
+ return function(collection, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ setter(result, value, callback(value, index, collection), collection);
+ }
+ } else {
+ baseEach(collection, function(value, key, collection) {
+ setter(result, value, callback(value, key, collection), collection);
+ });
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, either curries or invokes `func`
+ * with an optional `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of method flags to compose.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry`
+ * 8 - `_.curry` (bound)
+ * 16 - `_.partial`
+ * 32 - `_.partialRight`
+ * @param {Array} [partialArgs] An array of arguments to prepend to those
+ * provided to the new function.
+ * @param {Array} [partialRightArgs] An array of arguments to append to those
+ * provided to the new function.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new function.
+ */
+ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ isPartial = bitmask & 16,
+ isPartialRight = bitmask & 32;
+
+ if (!isBindKey && !isFunction(func)) {
+ throw new TypeError;
+ }
+ if (isPartial && !partialArgs.length) {
+ bitmask &= ~16;
+ isPartial = partialArgs = false;
+ }
+ if (isPartialRight && !partialRightArgs.length) {
+ bitmask &= ~32;
+ isPartialRight = partialRightArgs = false;
+ }
+ var bindData = func && func.__bindData__;
+ if (bindData && bindData !== true) {
+ // clone `bindData`
+ bindData = slice(bindData);
+ if (bindData[2]) {
+ bindData[2] = slice(bindData[2]);
+ }
+ if (bindData[3]) {
+ bindData[3] = slice(bindData[3]);
+ }
+ // set `thisBinding` is not previously bound
+ if (isBind && !(bindData[1] & 1)) {
+ bindData[4] = thisArg;
+ }
+ // set if previously bound but not currently (subsequent curried functions)
+ if (!isBind && bindData[1] & 1) {
+ bitmask |= 8;
+ }
+ // set curried arity if not yet set
+ if (isCurry && !(bindData[1] & 4)) {
+ bindData[5] = arity;
+ }
+ // append partial left arguments
+ if (isPartial) {
+ push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+ }
+ // append partial right arguments
+ if (isPartialRight) {
+ unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+ }
+ // merge flags
+ bindData[1] |= bitmask;
+ return createWrapper.apply(null, bindData);
+ }
+ // fast path for `_.bind`
+ var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+ return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+ }
+
+ /**
+ * Creates compiled iteration functions.
+ *
+ * @private
+ * @param {...Object} [options] The compile options object(s).
+ * @param {string} [options.array] Code to determine if the iterable is an array or array-like.
+ * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop.
+ * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration.
+ * @param {string} [options.args] A comma separated string of iteration function arguments.
+ * @param {string} [options.top] Code to execute before the iteration branches.
+ * @param {string} [options.loop] Code to execute in the object loop.
+ * @param {string} [options.bottom] Code to execute after the iteration branches.
+ * @returns {Function} Returns the compiled function.
+ */
+ function createIterator() {
+ // data properties
+ iteratorData.shadowedProps = shadowedProps;
+ iteratorData.support = support;
+
+ // iterator options
+ iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = '';
+ iteratorData.init = 'iterable';
+ iteratorData.useHas = true;
+
+ // merge options into a template data object
+ for (var object, index = 0; object = arguments[index]; index++) {
+ for (var key in object) {
+ iteratorData[key] = object[key];
+ }
+ }
+ var args = iteratorData.args;
+ iteratorData.firstArg = /^[^,]+/.exec(args)[0];
+
+ // create the function factory
+ var factory = Function(
+ 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' +
+ 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' +
+ 'objectTypes, nonEnumProps, stringClass, stringProto, toString',
+ 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}'
+ );
+
+ // return the compiled function
+ return factory(
+ baseCreateCallback, errorClass, errorProto, hasOwnProperty,
+ indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto,
+ objectTypes, nonEnumProps, stringClass, stringProto, toString
+ );
+ }
+
+ /**
+ * Used by `escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} match The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(match) {
+ return htmlEscapes[match];
+ }
+
+ /**
+ * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+ * customized, this method returns the custom method, otherwise it returns
+ * the `baseIndexOf` function.
+ *
+ * @private
+ * @returns {Function} Returns the "indexOf" function.
+ */
+ function getIndexOf() {
+ var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a native function.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+ */
+ function isNative(value) {
+ return typeof value == 'function' && reNative.test(value);
+ }
+
+ /**
+ * Sets `this` binding data on a given function.
+ *
+ * @private
+ * @param {Function} func The function to set data on.
+ * @param {Array} value The data array to set.
+ */
+ var setBindData = !defineProperty ? noop : function(func, value) {
+ descriptor.value = value;
+ defineProperty(func, '__bindData__', descriptor);
+ descriptor.value = null;
+ };
+
+ /**
+ * A fallback implementation of `isPlainObject` which checks if a given value
+ * is an object created by the `Object` constructor, assuming objects created
+ * by the `Object` constructor have no inherited enumerable properties and that
+ * there are no `Object.prototype` extensions.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ */
+ function shimIsPlainObject(value) {
+ var ctor,
+ result;
+
+ // avoid non Object objects, `arguments` objects, and DOM elements
+ if (!(value && toString.call(value) == objectClass) ||
+ (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) ||
+ (!support.argsClass && isArguments(value)) ||
+ (!support.nodeClass && isNode(value))) {
+ return false;
+ }
+ // IE < 9 iterates inherited properties before own properties. If the first
+ // iterated property is an object's own property then there are no inherited
+ // enumerable properties.
+ if (support.ownLast) {
+ forIn(value, function(value, key, object) {
+ result = hasOwnProperty.call(object, key);
+ return false;
+ });
+ return result !== false;
+ }
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ forIn(value, function(value, key) {
+ result = key;
+ });
+ return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+ }
+
+ /**
+ * Used by `unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} match The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ function unescapeHtmlChar(match) {
+ return htmlUnescapes[match];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Checks if `value` is an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })(1, 2, 3);
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == argsClass || false;
+ }
+ // fallback for browsers that can't detect `arguments` objects by [[Class]]
+ if (!support.argsClass) {
+ isArguments = function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false;
+ };
+ }
+
+ /**
+ * Checks if `value` is an array.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+ * @example
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ */
+ var isArray = nativeIsArray || function(value) {
+ return value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == arrayClass || false;
+ };
+
+ /**
+ * A fallback implementation of `Object.keys` which produces an array of the
+ * given object's own enumerable property names.
+ *
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ */
+ var shimKeys = createIterator({
+ 'args': 'object',
+ 'init': '[]',
+ 'top': 'if (!(objectTypes[typeof object])) return result',
+ 'loop': 'result.push(index)'
+ });
+
+ /**
+ * Creates an array composed of the own enumerable property names of an object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names.
+ * @example
+ *
+ * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+ */
+ var keys = !nativeKeys ? shimKeys : function(object) {
+ if (!isObject(object)) {
+ return [];
+ }
+ if ((support.enumPrototypes && typeof object == 'function') ||
+ (support.nonEnumArgs && object.length && isArguments(object))) {
+ return shimKeys(object);
+ }
+ return nativeKeys(object);
+ };
+
+ /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */
+ var eachIteratorOptions = {
+ 'args': 'collection, callback, thisArg',
+ 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",
+ 'array': "typeof length == 'number'",
+ 'keys': keys,
+ 'loop': 'if (callback(iterable[index], index, collection) === false) return result'
+ };
+
+ /** Reusable iterator options for `assign` and `defaults` */
+ var defaultsIteratorOptions = {
+ 'args': 'object, source, guard',
+ 'top':
+ 'var args = arguments,\n' +
+ ' argsIndex = 0,\n' +
+ " argsLength = typeof guard == 'number' ? 2 : args.length;\n" +
+ 'while (++argsIndex < argsLength) {\n' +
+ ' iterable = args[argsIndex];\n' +
+ ' if (iterable && objectTypes[typeof iterable]) {',
+ 'keys': keys,
+ 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]",
+ 'bottom': ' }\n}'
+ };
+
+ /** Reusable iterator options for `forIn` and `forOwn` */
+ var forOwnIteratorOptions = {
+ 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top,
+ 'array': false
+ };
+
+ /**
+ * Used to convert characters to HTML entities:
+ *
+ * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+ * don't require escaping in HTML and have no special meaning unless they're part
+ * of a tag or an unquoted attribute value.
+ * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+ */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
+
+ /** Used to match HTML entities and HTML characters */
+ var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+ reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+ /**
+ * A function compiled to iterate `arguments` objects, arrays, objects, and
+ * strings consistenly across environments, executing the callback for each
+ * element in the collection. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index|key, collection). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @private
+ * @type Function
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ */
+ var baseEach = createIterator(eachIteratorOptions);
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property assignments of previous
+ * sources. If a callback is provided it will be executed to produce the
+ * assigned values. The callback is bound to `thisArg` and invoked with two
+ * arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @alias extend
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize assigning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+ * // => { 'name': 'fred', 'employer': 'slate' }
+ *
+ * var defaults = _.partialRight(_.assign, function(a, b) {
+ * return typeof a == 'undefined' ? b : a;
+ * });
+ *
+ * var object = { 'name': 'barney' };
+ * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var assign = createIterator(defaultsIteratorOptions, {
+ 'top':
+ defaultsIteratorOptions.top.replace(';',
+ ';\n' +
+ "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" +
+ ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' +
+ "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" +
+ ' callback = args[--argsLength];\n' +
+ '}'
+ ),
+ 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]'
+ });
+
+ /**
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+ * be cloned, otherwise they will be assigned by reference. If a callback
+ * is provided it will be executed to produce the cloned values. If the
+ * callback returns `undefined` cloning will be handled by the method instead.
+ * The callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var shallow = _.clone(characters);
+ * shallow[0] === characters[0];
+ * // => true
+ *
+ * var deep = _.clone(characters, true);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * _.mixin({
+ * 'clone': _.partialRight(_.clone, function(value) {
+ * return _.isElement(value) ? value.cloneNode(false) : undefined;
+ * })
+ * });
+ *
+ * var clone = _.clone(document.body);
+ * clone.childNodes.length;
+ * // => 0
+ */
+ function clone(value, isDeep, callback, thisArg) {
+ // allows working with "Collections" methods without using their `index`
+ // and `collection` arguments for `isDeep` and `callback`
+ if (typeof isDeep != 'boolean' && isDeep != null) {
+ thisArg = callback;
+ callback = isDeep;
+ isDeep = false;
+ }
+ return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates a deep clone of `value`. If a callback is provided it will be
+ * executed to produce the cloned values. If the callback returns `undefined`
+ * cloning will be handled by the method instead. The callback is bound to
+ * `thisArg` and invoked with one argument; (value).
+ *
+ * Note: This method is loosely based on the structured clone algorithm. Functions
+ * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+ * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+ * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to deep clone.
+ * @param {Function} [callback] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the deep cloned value.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * var deep = _.cloneDeep(characters);
+ * deep[0] === characters[0];
+ * // => false
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'node': element
+ * };
+ *
+ * var clone = _.cloneDeep(view, function(value) {
+ * return _.isElement(value) ? value.cloneNode(true) : undefined;
+ * });
+ *
+ * clone.node == view.node;
+ * // => false
+ */
+ function cloneDeep(value, callback, thisArg) {
+ return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ }
+
+ /**
+ * Creates an object that inherits from the given `prototype` object. If a
+ * `properties` object is provided its own enumerable properties are assigned
+ * to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties ? assign(result, properties) : result;
+ }
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object for all destination properties that resolve to `undefined`. Once a
+ * property is set, additional defaults of the same property will be ignored.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param- {Object} [guard] Allows working with `_.reduce` without using its
+ * `key` and `object` arguments as sources.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var object = { 'name': 'barney' };
+ * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
+ */
+ var defaults = createIterator(defaultsIteratorOptions);
+
+ /**
+ * This method is like `_.findIndex` except that it returns the key of the
+ * first element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': false },
+ * 'fred': { 'age': 40, 'blocked': true },
+ * 'pebbles': { 'age': 1, 'blocked': false }
+ * };
+ *
+ * _.findKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => 'barney' (property order is not guaranteed across environments)
+ *
+ * // using "_.where" callback shorthand
+ * _.findKey(characters, { 'age': 1 });
+ * // => 'pebbles'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findKey(characters, 'blocked');
+ * // => 'fred'
+ */
+ function findKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * This method is like `_.findKey` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to search.
+ * @param {Function|Object|string} [callback=identity] The function called per
+ * iteration. If a property name or object is provided it will be used to
+ * create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': true },
+ * 'fred': { 'age': 40, 'blocked': false },
+ * 'pebbles': { 'age': 1, 'blocked': true }
+ * };
+ *
+ * _.findLastKey(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastKey(characters, { 'age': 40 });
+ * // => 'fred'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastKey(characters, 'blocked');
+ * // => 'pebbles'
+ */
+ function findLastKey(object, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forOwnRight(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over own and inherited enumerable properties of an object,
+ * executing the callback for each property. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, key, object). Callbacks may exit
+ * iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forIn(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+ */
+ var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, {
+ 'useHas': false
+ });
+
+ /**
+ * This method is like `_.forIn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
+ * };
+ *
+ * _.forInRight(new Shape, function(value, key) {
+ * console.log(key);
+ * });
+ * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+ */
+ function forInRight(object, callback, thisArg) {
+ var pairs = [];
+
+ forIn(object, function(value, key) {
+ pairs.push(key, value);
+ });
+
+ var length = pairs.length;
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(pairs[length--], pairs[length], object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Iterates over own enumerable properties of an object, executing the callback
+ * for each property. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, key, object). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+ */
+ var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions);
+
+ /**
+ * This method is like `_.forOwn` except that it iterates over elements
+ * of a `collection` in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+ * console.log(key);
+ * });
+ * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+ */
+ function forOwnRight(object, callback, thisArg) {
+ var props = keys(object),
+ length = props.length;
+
+ callback = baseCreateCallback(callback, thisArg, 3);
+ while (length--) {
+ var key = props[length];
+ if (callback(object[key], key, object) === false) {
+ break;
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Creates a sorted array of property names of all enumerable properties,
+ * own and inherited, of `object` that have function values.
+ *
+ * @static
+ * @memberOf _
+ * @alias methods
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property names that have function values.
+ * @example
+ *
+ * _.functions(_);
+ * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+ */
+ function functions(object) {
+ var result = [];
+ forIn(object, function(value, key) {
+ if (isFunction(value)) {
+ result.push(key);
+ }
+ });
+ return result.sort();
+ }
+
+ /**
+ * Checks if the specified property name exists as a direct property of `object`,
+ * instead of an inherited property.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to check.
+ * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+ * @example
+ *
+ * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+ * // => true
+ */
+ function has(object, key) {
+ return object ? hasOwnProperty.call(object, key) : false;
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of the given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the created inverted object.
+ * @example
+ *
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
+ */
+ function invert(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
+
+ while (++index < length) {
+ var key = props[index];
+ result[object[key]] = key;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if `value` is a boolean value.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+ * @example
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass || false;
+ }
+
+ /**
+ * Checks if `value` is a date.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ */
+ function isDate(value) {
+ return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+ }
+
+ /**
+ * Checks if `value` is a DOM element.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+ * @example
+ *
+ * _.isElement(document.body);
+ * // => true
+ */
+ function isElement(value) {
+ return value && value.nodeType === 1 || false;
+ }
+
+ /**
+ * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+ * length of `0` and objects with no own enumerable properties are considered
+ * "empty".
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object|string} value The value to inspect.
+ * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({});
+ * // => true
+ *
+ * _.isEmpty('');
+ * // => true
+ */
+ function isEmpty(value) {
+ var result = true;
+ if (!value) {
+ return result;
+ }
+ var className = toString.call(value),
+ length = value.length;
+
+ if ((className == arrayClass || className == stringClass ||
+ (support.argsClass ? className == argsClass : isArguments(value))) ||
+ (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+ return !length;
+ }
+ forOwn(value, function() {
+ return (result = false);
+ });
+ return result;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent to each other. If a callback is provided it will be executed
+ * to compare values. If the callback returns `undefined` comparisons will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (a, b).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} a The value to compare.
+ * @param {*} b The other value to compare.
+ * @param {Function} [callback] The function to customize comparing values.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var copy = { 'name': 'fred' };
+ *
+ * object == copy;
+ * // => false
+ *
+ * _.isEqual(object, copy);
+ * // => true
+ *
+ * var words = ['hello', 'goodbye'];
+ * var otherWords = ['hi', 'goodbye'];
+ *
+ * _.isEqual(words, otherWords, function(a, b) {
+ * var reGreet = /^(?:hello|hi)$/i,
+ * aGreet = _.isString(a) && reGreet.test(a),
+ * bGreet = _.isString(b) && reGreet.test(b);
+ *
+ * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * });
+ * // => true
+ */
+ function isEqual(a, b, callback, thisArg) {
+ return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+ }
+
+ /**
+ * Checks if `value` is, or can be coerced to, a finite number.
+ *
+ * Note: This is not the same as native `isFinite` which will return true for
+ * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+ }
+
+ /**
+ * Checks if `value` is a function.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ */
+ function isFunction(value) {
+ return typeof value == 'function';
+ }
+ // fallback for older versions of Chrome and Safari
+ if (isFunction(/x/)) {
+ isFunction = function(value) {
+ return typeof value == 'function' && toString.call(value) == funcClass;
+ };
+ }
+
+ /**
+ * Checks if `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+ function isObject(value) {
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.io/#x8
+ // and avoid a V8 bug
+ // http://code.google.com/p/v8/issues/detail?id=2291
+ return !!(value && objectTypes[typeof value]);
+ }
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * Note: This is not the same as native `isNaN` which will return `true` for
+ * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // `NaN` as a primitive is the only value that is not equal to itself
+ // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(undefined);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is a number.
+ *
+ * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(8.4 * 5);
+ * // => true
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ value && typeof value == 'object' && toString.call(value) == numberClass || false;
+ }
+
+ /**
+ * Checks if `value` is an object created by the `Object` constructor.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * _.isPlainObject(new Shape);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ */
+ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+ };
+
+ /**
+ * Checks if `value` is a regular expression.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+ * @example
+ *
+ * _.isRegExp(/fred/);
+ * // => true
+ */
+ function isRegExp(value) {
+ return value && objectTypes[typeof value] && toString.call(value) == regexpClass || false;
+ }
+
+ /**
+ * Checks if `value` is a string.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('fred');
+ * // => true
+ */
+ function isString(value) {
+ return typeof value == 'string' ||
+ value && typeof value == 'object' && toString.call(value) == stringClass || false;
+ }
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ */
+ function isUndefined(value) {
+ return typeof value == 'undefined';
+ }
+
+ /**
+ * Creates an object with the same keys as `object` and values generated by
+ * running each own enumerable property of `object` through the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+ * @example
+ *
+ * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ *
+ * var characters = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.mapValues(characters, 'age');
+ * // => { 'fred': 40, 'pebbles': 1 }
+ */
+ function mapValues(object, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ forOwn(object, function(value, key, object) {
+ result[key] = callback(value, key, object);
+ });
+ return result;
+ }
+
+ /**
+ * Recursively merges own enumerable properties of the source object(s), that
+ * don't resolve to `undefined` into the destination object. Subsequent sources
+ * will overwrite property assignments of previous sources. If a callback is
+ * provided it will be executed to produce the merged values of the destination
+ * and source properties. If the callback returns `undefined` merging will
+ * be handled by the method instead. The callback is bound to `thisArg` and
+ * invoked with two arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {...Object} [source] The source objects.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var names = {
+ * 'characters': [
+ * { 'name': 'barney' },
+ * { 'name': 'fred' }
+ * ]
+ * };
+ *
+ * var ages = {
+ * 'characters': [
+ * { 'age': 36 },
+ * { 'age': 40 }
+ * ]
+ * };
+ *
+ * _.merge(names, ages);
+ * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+ *
+ * var food = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var otherFood = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(food, otherFood, function(a, b) {
+ * return _.isArray(a) ? a.concat(b) : undefined;
+ * });
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+ */
+ function merge(object) {
+ var args = arguments,
+ length = 2;
+
+ if (!isObject(object)) {
+ return object;
+ }
+ // allows working with `_.reduce` and `_.reduceRight` without using
+ // their `index` and `collection` arguments
+ if (typeof args[2] != 'number') {
+ length = args.length;
+ }
+ if (length > 3 && typeof args[length - 2] == 'function') {
+ var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+ } else if (length > 2 && typeof args[length - 1] == 'function') {
+ callback = args[--length];
+ }
+ var sources = slice(arguments, 1, length),
+ index = -1,
+ stackA = getArray(),
+ stackB = getArray();
+
+ while (++index < length) {
+ baseMerge(object, sources[index], callback, stackA, stackB);
+ }
+ releaseArray(stackA);
+ releaseArray(stackB);
+ return object;
+ }
+
+ /**
+ * Creates a shallow clone of `object` excluding the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` omitting the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The properties to omit or the
+ * function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object without the omitted properties.
+ * @example
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+ * // => { 'name': 'fred' }
+ *
+ * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function omit(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var props = [];
+ forIn(object, function(value, key) {
+ props.push(key);
+ });
+ props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ result[key] = object[key];
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (!callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates a two dimensional array of an object's key-value pairs,
+ * i.e. `[[key1, value1], [key2, value2]]`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns new array of key-value pairs.
+ * @example
+ *
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
+ }
+
+ /**
+ * Creates a shallow clone of `object` composed of the specified properties.
+ * Property names may be specified as individual arguments or as arrays of
+ * property names. If a callback is provided it will be executed for each
+ * property of `object` picking the properties the callback returns truey
+ * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, key, object).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The source object.
+ * @param {Function|...string|string[]} [callback] The function called per
+ * iteration or property names to pick, specified as individual property
+ * names or arrays of property names.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
+ * @example
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+ * // => { 'name': 'fred' }
+ *
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'fred' }
+ */
+ function pick(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var index = -1,
+ props = baseFlatten(arguments, true, false, 1),
+ length = isObject(object) ? props.length : 0;
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
+ }
+ } else {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * An alternative to `_.reduce` this method transforms `object` to a new
+ * `accumulator` object which is the result of running each of its own
+ * enumerable properties through a callback, with each callback execution
+ * potentially mutating the `accumulator` object. The callback is bound to
+ * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+ * Callbacks may exit iteration early by explicitly returning `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Array|Object} object The object to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] The custom accumulator value.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+ * num *= num;
+ * if (num % 2) {
+ * return result.push(num) < 3;
+ * }
+ * });
+ * // => [1, 9, 25]
+ *
+ * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * });
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function transform(object, callback, accumulator, thisArg) {
+ var isArr = isArray(object);
+ if (accumulator == null) {
+ if (isArr) {
+ accumulator = [];
+ } else {
+ var ctor = object && object.constructor,
+ proto = ctor && ctor.prototype;
+
+ accumulator = baseCreate(proto);
+ }
+ }
+ if (callback) {
+ callback = lodash.createCallback(callback, thisArg, 4);
+ (isArr ? baseEach : forOwn)(object, function(value, index, object) {
+ return callback(accumulator, value, index, object);
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * Creates an array composed of the own enumerable property values of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns an array of property values.
+ * @example
+ *
+ * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => [1, 2, 3] (property order is not guaranteed across environments)
+ */
+ function values(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of elements from the specified indexes, or keys, of the
+ * `collection`. Indexes may be specified as individual arguments or as arrays
+ * of indexes.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+ * to retrieve, specified as individual indexes or arrays of indexes.
+ * @returns {Array} Returns a new array of elements corresponding to the
+ * provided indexes.
+ * @example
+ *
+ * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+ * // => ['a', 'c', 'e']
+ *
+ * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+ * // => ['fred', 'pebbles']
+ */
+ function at(collection) {
+ var args = arguments,
+ index = -1,
+ props = baseFlatten(args, true, false, 1),
+ length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+ result = Array(length);
+
+ if (support.unindexedChars && isString(collection)) {
+ collection = collection.split('');
+ }
+ while(++index < length) {
+ result[index] = collection[props[index]];
+ }
+ return result;
+ }
+
+ /**
+ * Checks if a given value is present in a collection using strict equality
+ * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+ * offset from the end of the collection.
+ *
+ * @static
+ * @memberOf _
+ * @alias include
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {*} target The value to check for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+ * @example
+ *
+ * _.contains([1, 2, 3], 1);
+ * // => true
+ *
+ * _.contains([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+ * // => true
+ *
+ * _.contains('pebbles', 'eb');
+ * // => true
+ */
+ function contains(collection, target, fromIndex) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = collection ? collection.length : 0,
+ result = false;
+
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+ if (isArray(collection)) {
+ result = indexOf(collection, target, fromIndex) > -1;
+ } else if (typeof length == 'number') {
+ result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+ } else {
+ baseEach(collection, function(value) {
+ if (++index >= fromIndex) {
+ return !(result = value === target);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` through the callback. The corresponding value
+ * of each key is the number of times the key was returned by the callback.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': 1, '6': 2 }
+ *
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+ });
+
+ /**
+ * Checks if the given callback returns truey value for **all** elements of
+ * a collection. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if all elements passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes']);
+ * // => false
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(characters, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(characters, { 'age': 36 });
+ * // => false
+ */
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ return (result = !!callback(value, index, collection));
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning an array of all elements
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that passed the callback check.
+ * @example
+ *
+ * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [2, 4, 6]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.filter(characters, 'blocked');
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ */
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, returning the first element that
+ * the callback returns truey for. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect, findWhere
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.find(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+ *
+ * // using "_.where" callback shorthand
+ * _.find(characters, { 'age': 1 });
+ * // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(characters, 'blocked');
+ * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+ */
+ function find(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ return value;
+ }
+ }
+ } else {
+ var result;
+ baseEach(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+ }
+
+ /**
+ * This method is like `_.find` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the found element, else `undefined`.
+ * @example
+ *
+ * _.findLast([1, 2, 3, 4], function(num) {
+ * return num % 2 == 1;
+ * });
+ * // => 3
+ */
+ function findLast(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forEachRight(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Iterates over elements of a collection, executing the callback for each
+ * element. The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection). Callbacks may exit iteration early by
+ * explicitly returning `false`.
+ *
+ * Note: As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+ * // => logs each number and returns '1,2,3'
+ *
+ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+ * // => logs each number and returns the object (property order is not guaranteed across environments)
+ */
+ function forEach(collection, callback, thisArg) {
+ if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if (callback(collection[index], index, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ baseEach(collection, callback, thisArg);
+ }
+ return collection;
+ }
+
+ /**
+ * This method is like `_.forEach` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias eachRight
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+ * // => logs each number from right to left and returns '3,2,1'
+ */
+ function forEachRight(collection, callback, thisArg) {
+ var iterable = collection,
+ length = collection ? collection.length : 0;
+
+ callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+ if (isArray(collection)) {
+ while (length--) {
+ if (callback(collection[length], length, collection) === false) {
+ break;
+ }
+ }
+ } else {
+ if (typeof length != 'number') {
+ var props = keys(collection);
+ length = props.length;
+ } else if (support.unindexedChars && isString(collection)) {
+ iterable = collection.split('');
+ }
+ baseEach(collection, function(value, key, collection) {
+ key = props ? props[--length] : --length;
+ return callback(iterable[key], key, collection);
+ });
+ }
+ return collection;
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of a collection through the callback. The corresponding value
+ * of each key is an array of the elements responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+ * // => { '4': [4.2], '6': [6.1, 6.4] }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+ });
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of the collection through the given callback. The corresponding
+ * value of each key is the last element responsible for generating the key.
+ * The callback is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var keys = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.indexBy(keys, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ */
+ var indexBy = createAggregator(function(result, value, key) {
+ result[key] = value;
+ });
+
+ /**
+ * Invokes the method named by `methodName` on each element in the `collection`
+ * returning an array of the results of each invoked method. Additional arguments
+ * will be provided to each invoked method. If `methodName` is a function it
+ * will be invoked for, and `this` bound to, each element in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|string} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [arg] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of the results of each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ var args = slice(arguments, 2),
+ index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+ });
+ return result;
+ }
+
+ /**
+ * Creates an array of values by running each element in the collection
+ * through the callback. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @example
+ *
+ * _.map([1, 2, 3], function(num) { return num * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+ * // => [3, 6, 9] (property order is not guaranteed across environments)
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ if (isArray(collection)) {
+ while (++index < length) {
+ result[index] = callback(collection[index], index, collection);
+ }
+ } else {
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the maximum value of a collection. If the collection is empty or
+ * falsey `-Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.max(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'fred', 'age': 40 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.max(characters, 'age');
+ * // => { 'name': 'fred', 'age': 40 };
+ */
+ function max(collection, callback, thisArg) {
+ var computed = -Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ baseEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current > computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the minimum value of a collection. If the collection is empty or
+ * falsey `Infinity` is returned. If a callback is provided it will be executed
+ * for each value in the collection to generate the criterion by which the value
+ * is ranked. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.min(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'barney', 'age': 36 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.min(characters, 'age');
+ * // => { 'name': 'barney', 'age': 36 };
+ */
+ function min(collection, callback, thisArg) {
+ var computed = Infinity,
+ result = computed;
+
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (callback == null && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg, 3);
+
+ baseEach(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current < computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the value of a specified property from all elements in the collection.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {string} property The name of the property to pluck.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * _.pluck(characters, 'name');
+ * // => ['barney', 'fred']
+ */
+ var pluck = map;
+
+ /**
+ * Reduces a collection to a value which is the accumulated result of running
+ * each element in the collection through the callback, where each successive
+ * callback execution consumes the return value of the previous execution. If
+ * `accumulator` is not provided the first element of the collection will be
+ * used as the initial `accumulator` value. The callback is bound to `thisArg`
+ * and invoked with four arguments; (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function reduce(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ if (noaccum) {
+ accumulator = collection[++index];
+ }
+ while (++index < length) {
+ accumulator = callback(accumulator, collection[index], index, collection);
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection)
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * This method is like `_.reduce` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {*} [accumulator] Initial value of the accumulator.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the accumulated value.
+ * @example
+ *
+ * var list = [[0, 1], [2, 3], [4, 5]];
+ * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
+ forEachRight(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The opposite of `_.filter` this method returns the elements of a
+ * collection that the callback does **not** return truey for.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that failed the callback check.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [1, 3, 5]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.reject(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+ *
+ * // using "_.where" callback shorthand
+ * _.reject(characters, { 'age': 36 });
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+ */
+ function reject(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ return filter(collection, function(value, index, collection) {
+ return !callback(value, index, collection);
+ });
+ }
+
+ /**
+ * Retrieves a random element or `n` random elements from a collection.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to sample.
+ * @param {number} [n] The number of elements to sample.
+ * @param- {Object} [guard] Allows working with functions like `_.map`
+ * without using their `index` arguments as `n`.
+ * @returns {Array} Returns the random sample(s) of `collection`.
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ *
+ * _.sample([1, 2, 3, 4], 2);
+ * // => [3, 1]
+ */
+ function sample(collection, n, guard) {
+ if (collection && typeof collection.length != 'number') {
+ collection = values(collection);
+ } else if (support.unindexedChars && isString(collection)) {
+ collection = collection.split('');
+ }
+ if (n == null || guard) {
+ return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+ }
+ var result = shuffle(collection);
+ result.length = nativeMin(nativeMax(0, n), result.length);
+ return result;
+ }
+
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(collection) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ forEach(collection, function(value) {
+ var rand = baseRandom(0, ++index);
+ result[index] = result[rand];
+ result[rand] = value;
+ });
+ return result;
+ }
+
+ /**
+ * Gets the size of the `collection` by returning `collection.length` for arrays
+ * and array-like objects or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns `collection.length` or number of own enumerable properties.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return typeof length == 'number' ? length : keys(collection).length;
+ }
+
+ /**
+ * Checks if the callback returns a truey value for **any** element of a
+ * collection. The function returns as soon as it finds a passing value and
+ * does not iterate over the entire collection. The callback is bound to
+ * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {boolean} Returns `true` if any element passed the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.some(characters, 'blocked');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.some(characters, { 'age': 1 });
+ * // => false
+ */
+ function some(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg, 3);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if ((result = callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ baseEach(collection, function(value, index, collection) {
+ return !(result = callback(value, index, collection));
+ });
+ }
+ return !!result;
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through the callback. This method
+ * performs a stable sort, that is, it will preserve the original sort order
+ * of equal elements. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an array of property names is provided for `callback` the collection
+ * will be sorted by each property value.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of sorted elements.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+ * // => [3, 1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 26 },
+ * { 'name': 'fred', 'age': 30 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(_.sortBy(characters, 'age'), _.values);
+ * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+ *
+ * // sorting by multiple properties
+ * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+ * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+ */
+ function sortBy(collection, callback, thisArg) {
+ var index = -1,
+ isArr = isArray(callback),
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
+
+ if (!isArr) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ forEach(collection, function(value, key, collection) {
+ var object = result[++index] = getObject();
+ if (isArr) {
+ object.criteria = map(callback, function(key) { return value[key]; });
+ } else {
+ (object.criteria = getArray())[0] = callback(value, key, collection);
+ }
+ object.index = index;
+ object.value = value;
+ });
+
+ length = result.length;
+ result.sort(compareAscending);
+ while (length--) {
+ var object = result[length];
+ result[length] = object.value;
+ if (!isArr) {
+ releaseArray(object.criteria);
+ }
+ releaseObject(object);
+ }
+ return result;
+ }
+
+ /**
+ * Converts the `collection` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to convert.
+ * @returns {Array} Returns the new converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+ * // => [2, 3, 4]
+ */
+ function toArray(collection) {
+ if (collection && typeof collection.length == 'number') {
+ return (support.unindexedChars && isString(collection))
+ ? collection.split('')
+ : slice(collection);
+ }
+ return values(collection);
+ }
+
+ /**
+ * Performs a deep comparison of each element in a `collection` to the given
+ * `properties` object, returning an array of all elements that have equivalent
+ * property values.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Object} props The object of property values to filter by.
+ * @returns {Array} Returns a new array of elements that have the given properties.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * _.where(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+ *
+ * _.where(characters, { 'pets': ['dino'] });
+ * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+ */
+ var where = filter;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are all falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array excluding all values of the provided arrays using strict
+ * equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {...Array} [values] The arrays of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ * // => [1, 3, 4]
+ */
+ function difference(array) {
+ return baseDifference(array, baseFlatten(arguments, true, true, 1));
+ }
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element that passes the callback check, instead of the element itself.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.findIndex(characters, function(chr) {
+ * return chr.age < 20;
+ * });
+ * // => 2
+ *
+ * // using "_.where" callback shorthand
+ * _.findIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findIndex(characters, 'blocked');
+ * // => 1
+ */
+ function findIndex(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ if (callback(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of a `collection` from right to left.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': true },
+ * { 'name': 'fred', 'age': 40, 'blocked': false },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': true }
+ * ];
+ *
+ * _.findLastIndex(characters, function(chr) {
+ * return chr.age > 30;
+ * });
+ * // => 1
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastIndex(characters, 'blocked');
+ * // => 2
+ */
+ function findLastIndex(array, callback, thisArg) {
+ var length = array ? array.length : 0;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (length--) {
+ if (callback(array[length], length, array)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the first element or first `n` elements of an array. If a callback
+ * is provided elements at the beginning of the array are returned as long
+ * as the callback returns truey. The callback is bound to `thisArg` and
+ * invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head, take
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the first element(s) of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.first([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.first(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function first(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = -1;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[0] : undefined;
+ }
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, n), length));
+ }
+
+ /**
+ * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+ * is truey, the array will only be flattened a single level. If a callback
+ * is provided each element of the array is passed through the callback before
+ * flattening. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to flatten.
+ * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ *
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.flatten(characters, 'pets');
+ * // => ['hoppy', 'baby puss', 'dino']
+ */
+ function flatten(array, isShallow, callback, thisArg) {
+ // juggle arguments
+ if (typeof isShallow != 'boolean' && isShallow != null) {
+ thisArg = callback;
+ callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+ isShallow = false;
+ }
+ if (callback != null) {
+ array = map(array, callback, thisArg);
+ }
+ return baseFlatten(array, isShallow);
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found using
+ * strict equality for comparisons, i.e. `===`. If the array is already sorted
+ * providing `true` for `fromIndex` will run a faster binary search.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ if (typeof fromIndex == 'number') {
+ var length = array ? array.length : 0;
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+ } else if (fromIndex) {
+ var index = sortedIndex(array, value);
+ return array[index] === value ? index : -1;
+ }
+ return baseIndexOf(array, value, fromIndex);
+ }
+
+ /**
+ * Gets all but the last element or last `n` elements of an array. If a
+ * callback is provided elements at the end of the array are excluded from
+ * the result as long as the callback returns truey. The callback is bound
+ * to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.initial([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.initial([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.initial(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ function initial(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : callback || n;
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+ }
+
+ /**
+ * Creates an array of unique values present in all provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of shared values.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection() {
+ var args = [],
+ argsIndex = -1,
+ argsLength = arguments.length,
+ caches = getArray(),
+ indexOf = getIndexOf(),
+ trustIndexOf = indexOf === baseIndexOf,
+ seen = getArray();
+
+ while (++argsIndex < argsLength) {
+ var value = arguments[argsIndex];
+ if (isArray(value) || isArguments(value)) {
+ args.push(value);
+ caches.push(trustIndexOf && value.length >= largeArraySize &&
+ createCache(argsIndex ? args[argsIndex] : seen));
+ }
+ }
+ var array = args[0],
+ index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ outer:
+ while (++index < length) {
+ var cache = caches[0];
+ value = array[index];
+
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+ argsIndex = argsLength;
+ (cache || seen).push(value);
+ while (--argsIndex) {
+ cache = caches[argsIndex];
+ if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ }
+ while (argsLength--) {
+ cache = caches[argsLength];
+ if (cache) {
+ releaseObject(cache);
+ }
+ }
+ releaseArray(caches);
+ releaseArray(seen);
+ return result;
+ }
+
+ /**
+ * Gets the last element or last `n` elements of an array. If a callback is
+ * provided elements at the end of the array are returned as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {*} Returns the last element(s) of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ *
+ * _.last([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.last([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.last(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.last(characters, { 'employer': 'na' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function last(array, callback, thisArg) {
+ var n = 0,
+ length = array ? array.length : 0;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array ? array[length - 1] : undefined;
+ }
+ }
+ return slice(array, nativeMax(0, length - n));
+ }
+
+ /**
+ * Gets the index at which the last occurrence of `value` is found using strict
+ * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+ * as the offset from the end of the collection.
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var index = array ? array.length : 0;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all provided values from the given array using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {...*} [value] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3, 1, 2, 3];
+ * _.pull(array, 2, 3);
+ * console.log(array);
+ * // => [1, 1]
+ */
+ function pull(array) {
+ var args = arguments,
+ argsIndex = 0,
+ argsLength = args.length,
+ length = array ? array.length : 0;
+
+ while (++argsIndex < argsLength) {
+ var index = -1,
+ value = args[argsIndex];
+ while (++index < length) {
+ if (array[index] === value) {
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`. If `start` is less than `stop` a
+ * zero-length range is created unless a negative `step` is specified.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns a new range array.
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = typeof step == 'number' ? step : (+step || 1);
+
+ if (end == null) {
+ end = start;
+ start = 0;
+ }
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(0, ceil((end - start) / (step || 1))),
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
+ }
+
+ /**
+ * Removes all elements from an array that the callback returns truey for
+ * and returns an array of removed elements. The callback is bound to `thisArg`
+ * and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4, 5, 6];
+ * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+ *
+ * console.log(array);
+ * // => [1, 3, 5]
+ *
+ * console.log(evens);
+ * // => [2, 4, 6]
+ */
+ function remove(array, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length) {
+ var value = array[index];
+ if (callback(value, index, array)) {
+ result.push(value);
+ splice.call(array, index--, 1);
+ length--;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The opposite of `_.initial` this method gets all but the first element or
+ * first `n` elements of an array. If a callback function is provided elements
+ * at the beginning of the array are excluded from the result as long as the
+ * callback returns truey. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias drop, tail
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|number|string} [callback=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is provided it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.rest([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.rest([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.rest(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.rest(characters, { 'employer': 'slate' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+ */
+ function rest(array, callback, thisArg) {
+ if (typeof callback != 'number' && callback != null) {
+ var n = 0,
+ index = -1,
+ length = array ? array.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg, 3);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+ }
+ return slice(array, n);
+ }
+
+ /**
+ * Uses a binary search to determine the smallest index at which a value
+ * should be inserted into a given sorted array in order to maintain the sort
+ * order of the array. If a callback is provided it will be executed for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * callback is bound to `thisArg` and invoked with one argument; (value).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([20, 30, 50], 40);
+ * // => 2
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 2
+ *
+ * var dict = {
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * };
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return dict.wordToNumber[word];
+ * });
+ * // => 2
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return this.wordToNumber[word];
+ * }, dict);
+ * // => 2
+ */
+ function sortedIndex(array, value, callback, thisArg) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ // explicitly reference `identity` for better inlining in Firefox
+ callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+ value = callback(value);
+
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ (callback(array[mid]) < value)
+ ? low = mid + 1
+ : high = mid;
+ }
+ return low;
+ }
+
+ /**
+ * Creates an array of unique values, in order, of the provided arrays using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of combined values.
+ * @example
+ *
+ * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+ * // => [1, 2, 3, 5, 4]
+ */
+ function union() {
+ return baseUniq(baseFlatten(arguments, true, true));
+ }
+
+ /**
+ * Creates a duplicate-value-free version of an array using strict equality
+ * for comparisons, i.e. `===`. If the array is sorted, providing
+ * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+ * each element of `array` is passed through the callback before uniqueness
+ * is computed. The callback is bound to `thisArg` and invoked with three
+ * arguments; (value, index, array).
+ *
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+ * @param {Function|Object|string} [callback=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1, 3, 1]);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 1, 2, 2, 3], true);
+ * // => [1, 2, 3]
+ *
+ * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+ * // => ['A', 'b', 'C']
+ *
+ * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+ * // => [1, 2.5, 3]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, callback, thisArg) {
+ // juggle arguments
+ if (typeof isSorted != 'boolean' && isSorted != null) {
+ thisArg = callback;
+ callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+ isSorted = false;
+ }
+ if (callback != null) {
+ callback = lodash.createCallback(callback, thisArg, 3);
+ }
+ return baseUniq(array, isSorted, callback);
+ }
+
+ /**
+ * Creates an array excluding all provided values using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to filter.
+ * @param {...*} [value] The values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ return baseDifference(array, slice(arguments, 1));
+ }
+
+ /**
+ * Creates an array that is the symmetric difference of the provided arrays.
+ * See http://en.wikipedia.org/wiki/Symmetric_difference.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {...Array} [array] The arrays to inspect.
+ * @returns {Array} Returns an array of values.
+ * @example
+ *
+ * _.xor([1, 2, 3], [5, 2, 1, 4]);
+ * // => [3, 5, 4]
+ *
+ * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+ * // => [1, 4, 5]
+ */
+ function xor() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var array = arguments[index];
+ if (isArray(array) || isArguments(array)) {
+ var result = result
+ ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+ : array;
+ }
+ }
+ return result || [];
+ }
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the first
+ * elements of the given arrays, the second of which contains the second
+ * elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @alias unzip
+ * @category Arrays
+ * @param {...Array} [array] Arrays to process.
+ * @returns {Array} Returns a new array of grouped elements.
+ * @example
+ *
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
+ */
+ function zip() {
+ var array = arguments.length > 1 ? arguments : arguments[0],
+ index = -1,
+ length = array ? max(pluck(array, 'length')) : 0,
+ result = Array(length < 0 ? 0 : length);
+
+ while (++index < length) {
+ result[index] = pluck(array, index);
+ }
+ return result;
+ }
+
+ /**
+ * Creates an object composed from arrays of `keys` and `values`. Provide
+ * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+ * or two arrays, one of `keys` and one of corresponding `values`.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Arrays
+ * @param {Array} keys The array of keys.
+ * @param {Array} [values=[]] The array of values.
+ * @returns {Object} Returns an object composed of the given keys and
+ * corresponding values.
+ * @example
+ *
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ function zipObject(keys, values) {
+ var index = -1,
+ length = keys ? keys.length : 0,
+ result = {};
+
+ if (!values && length && !isArray(keys[0])) {
+ values = [];
+ }
+ while (++index < length) {
+ var key = keys[index];
+ if (values) {
+ result[key] = values[index];
+ } else if (key) {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that executes `func`, with the `this` binding and
+ * arguments of the created function, only after being called `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {number} n The number of times the function must be called before
+ * `func` is executed.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('Done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => logs 'Done saving!', after all saves have completed
+ */
+ function after(n, func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with the `this`
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * provided to the bound function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var func = function(greeting) {
+ * return greeting + ' ' + this.name;
+ * };
+ *
+ * func = _.bind(func, { 'name': 'fred' }, 'hi');
+ * func();
+ * // => 'hi fred'
+ */
+ function bind(func, thisArg) {
+ return arguments.length > 2
+ ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+ : createWrapper(func, 1, null, null, thisArg);
+ }
+
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing
+ * method. Method names may be specified as individual arguments or as arrays
+ * of method names. If no method names are provided all the function properties
+ * of `object` will be bound.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {...string} [methodName] The object method names to
+ * bind, specified as individual method names or arrays of method names.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { console.log('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => logs 'clicked docs', when the button is clicked
+ */
+ function bindAll(object) {
+ var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+ index = -1,
+ length = funcs.length;
+
+ while (++index < length) {
+ var key = funcs[index];
+ object[key] = createWrapper(object[key], 1, null, null, object);
+ }
+ return object;
+ }
+
+ /**
+ * Creates a function that, when called, invokes the method at `object[key]`
+ * and prepends any additional `bindKey` arguments to those provided to the bound
+ * function. This method differs from `_.bind` by allowing bound functions to
+ * reference methods that will be redefined or don't yet exist.
+ * See http://michaux.ca/articles/lazy-function-definition-pattern.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object the method belongs to.
+ * @param {string} key The key of the method.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'name': 'fred',
+ * 'greet': function(greeting) {
+ * return greeting + ' ' + this.name;
+ * }
+ * };
+ *
+ * var func = _.bindKey(object, 'greet', 'hi');
+ * func();
+ * // => 'hi fred'
+ *
+ * object.greet = function(greeting) {
+ * return greeting + 'ya ' + this.name + '!';
+ * };
+ *
+ * func();
+ * // => 'hiya fred!'
+ */
+ function bindKey(object, key) {
+ return arguments.length > 2
+ ? createWrapper(key, 19, slice(arguments, 2), null, object)
+ : createWrapper(key, 3, null, null, object);
+ }
+
+ /**
+ * Creates a function that is the composition of the provided functions,
+ * where each function consumes the return value of the function that follows.
+ * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * Each function is executed with the `this` binding of the composed function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {...Function} [func] Functions to compose.
+ * @returns {Function} Returns the new composed function.
+ * @example
+ *
+ * var realNameMap = {
+ * 'pebbles': 'penelope'
+ * };
+ *
+ * var format = function(name) {
+ * name = realNameMap[name.toLowerCase()] || name;
+ * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+ * };
+ *
+ * var greet = function(formatted) {
+ * return 'Hiya ' + formatted + '!';
+ * };
+ *
+ * var welcome = _.compose(greet, format);
+ * welcome('pebbles');
+ * // => 'Hiya Penelope!'
+ */
+ function compose() {
+ var funcs = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ if (!isFunction(funcs[length])) {
+ throw new TypeError;
+ }
+ }
+ return function() {
+ var args = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ args = [funcs[length].apply(this, args)];
+ }
+ return args[0];
+ };
+ }
+
+ /**
+ * Creates a function which accepts one or more arguments of `func` that when
+ * invoked either executes `func` returning its result, if all `func` arguments
+ * have been provided, or returns a function that accepts one or more of the
+ * remaining `func` arguments, and so on. The arity of `func` can be specified
+ * if `func.length` is not sufficient.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var curried = _.curry(function(a, b, c) {
+ * console.log(a + b + c);
+ * });
+ *
+ * curried(1)(2)(3);
+ * // => 6
+ *
+ * curried(1, 2)(3);
+ * // => 6
+ *
+ * curried(1, 2, 3);
+ * // => 6
+ */
+ function curry(func, arity) {
+ arity = typeof arity == 'number' ? arity : (+arity || func.length);
+ return createWrapper(func, 4, null, null, null, arity);
+ }
+
+ /**
+ * Creates a function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked.
+ * Provide an options object to indicate that `func` should be invoked on
+ * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+ * to the debounced function will return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the debounced function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to delay.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+ * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // avoid costly calculations while the window size is in flux
+ * var lazyLayout = _.debounce(calculateLayout, 150);
+ * jQuery(window).on('resize', lazyLayout);
+ *
+ * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+ * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * });
+ *
+ * // ensure `batchLog` is executed once after 1 second of debounced calls
+ * var source = new EventSource('/stream');
+ * source.addEventListener('message', _.debounce(batchLog, 250, {
+ * 'maxWait': 1000
+ * }, false);
+ */
+ function debounce(func, wait, options) {
+ var args,
+ maxTimeoutId,
+ result,
+ stamp,
+ thisArg,
+ timeoutId,
+ trailingCall,
+ lastCalled = 0,
+ maxWait = false,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ wait = nativeMax(0, wait) || 0;
+ if (options === true) {
+ var leading = true;
+ trailing = false;
+ } else if (isObject(options)) {
+ leading = options.leading;
+ maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ var delayed = function() {
+ var remaining = wait - (now() - stamp);
+ if (remaining <= 0) {
+ if (maxTimeoutId) {
+ clearTimeout(maxTimeoutId);
+ }
+ var isCalled = trailingCall;
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (isCalled) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ } else {
+ timeoutId = setTimeout(delayed, remaining);
+ }
+ };
+
+ var maxDelayed = function() {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ maxTimeoutId = timeoutId = trailingCall = undefined;
+ if (trailing || (maxWait !== wait)) {
+ lastCalled = now();
+ result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ }
+ };
+
+ return function() {
+ args = arguments;
+ stamp = now();
+ thisArg = this;
+ trailingCall = trailing && (timeoutId || !leading);
+
+ if (maxWait === false) {
+ var leadingCall = leading && !timeoutId;
+ } else {
+ if (!maxTimeoutId && !leading) {
+ lastCalled = stamp;
+ }
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0;
+
+ if (isCalled) {
+ if (maxTimeoutId) {
+ maxTimeoutId = clearTimeout(maxTimeoutId);
+ }
+ lastCalled = stamp;
+ result = func.apply(thisArg, args);
+ }
+ else if (!maxTimeoutId) {
+ maxTimeoutId = setTimeout(maxDelayed, remaining);
+ }
+ }
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
+ timeoutId = setTimeout(delayed, wait);
+ }
+ if (leadingCall) {
+ isCalled = true;
+ result = func.apply(thisArg, args);
+ }
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Defers executing the `func` function until the current call stack has cleared.
+ * Additional arguments will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to defer.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) { console.log(text); }, 'deferred');
+ * // logs 'deferred' after one or more milliseconds
+ */
+ function defer(func) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 1);
+ return setTimeout(function() { func.apply(undefined, args); }, 1);
+ }
+
+ /**
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * will be provided to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay execution.
+ * @param {...*} [arg] Arguments to invoke the function with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) { console.log(text); }, 1000, 'later');
+ * // => logs 'later' after one second
+ */
+ function delay(func, wait) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var args = slice(arguments, 2);
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided it will be used to determine the cache key for storing the result
+ * based on the arguments provided to the memoized function. By default, the
+ * first argument provided to the memoized function is used as the cache key.
+ * The `func` is executed with the `this` binding of the memoized function.
+ * The result cache is exposed as the `cache` property on the memoized function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] A function used to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var fibonacci = _.memoize(function(n) {
+ * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ * });
+ *
+ * fibonacci(9)
+ * // => 34
+ *
+ * var data = {
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
+ * };
+ *
+ * // modifying the result cache
+ * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+ * get('pebbles');
+ * // => { 'name': 'pebbles', 'age': 1 }
+ *
+ * get.cache.pebbles.name = 'penelope';
+ * get('pebbles');
+ * // => { 'name': 'penelope', 'age': 1 }
+ */
+ function memoize(func, resolver) {
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ var memoized = function() {
+ var cache = memoized.cache,
+ key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+ return hasOwnProperty.call(cache, key)
+ ? cache[key]
+ : (cache[key] = func.apply(this, arguments));
+ }
+ memoized.cache = {};
+ return memoized;
+ }
+
+ /**
+ * Creates a function that is restricted to execute `func` once. Repeat calls to
+ * the function will return the value of the first call. The `func` is executed
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` executes `createApplication` once
+ */
+ function once(func) {
+ var ran,
+ result;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ return function() {
+ if (ran) {
+ return result;
+ }
+ ran = true;
+ result = func.apply(this, arguments);
+
+ // clear the `func` variable so the function may be garbage collected
+ func = null;
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those provided to the new function. This
+ * method is similar to `_.bind` except it does **not** alter the `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ' ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('fred');
+ * // => 'hi fred'
+ */
+ function partial(func) {
+ return createWrapper(func, 16, slice(arguments, 1));
+ }
+
+ /**
+ * This method is like `_.partial` except that `partial` arguments are
+ * appended to those provided to the new function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [arg] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+ *
+ * var options = {
+ * 'variable': 'data',
+ * 'imports': { 'jq': $ }
+ * };
+ *
+ * defaultsDeep(options, _.templateSettings);
+ *
+ * options.variable
+ * // => 'data'
+ *
+ * options.imports
+ * // => { '_': _, 'jq': $ }
+ */
+ function partialRight(func) {
+ return createWrapper(func, 32, null, slice(arguments, 1));
+ }
+
+ /**
+ * Creates a function that, when executed, will only call the `func` function
+ * at most once per every `wait` milliseconds. Provide an options object to
+ * indicate that `func` should be invoked on the leading and/or trailing edge
+ * of the `wait` timeout. Subsequent calls to the throttled function will
+ * return the result of the last `func` call.
+ *
+ * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * on the trailing edge of the timeout only if the the throttled function is
+ * invoked more than once during the `wait` timeout.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to throttle.
+ * @param {number} wait The number of milliseconds to throttle executions to.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // avoid excessively updating the position while scrolling
+ * var throttled = _.throttle(updatePosition, 100);
+ * jQuery(window).on('scroll', throttled);
+ *
+ * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+ * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+ * 'trailing': false
+ * }));
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (!isFunction(func)) {
+ throw new TypeError;
+ }
+ if (options === false) {
+ leading = false;
+ } else if (isObject(options)) {
+ leading = 'leading' in options ? options.leading : leading;
+ trailing = 'trailing' in options ? options.trailing : trailing;
+ }
+ debounceOptions.leading = leading;
+ debounceOptions.maxWait = wait;
+ debounceOptions.trailing = trailing;
+
+ return debounce(func, wait, debounceOptions);
+ }
+
+ /**
+ * Creates a function that provides `value` to the wrapper function as its
+ * first argument. Additional arguments provided to the function are appended
+ * to those provided to the wrapper function. The wrapper is executed with
+ * the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {*} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '<p>' + func(text) + '</p>';
+ * });
+ *
+ * p('Fred, Wilma, & Pebbles');
+ * // => '<p>Fred, Wilma, & Pebbles</p>'
+ */
+ function wrap(value, wrapper) {
+ return createWrapper(wrapper, 16, [value]);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that returns `value`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value The value to return from the new function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * var getter = _.constant(object);
+ * getter() === object;
+ * // => true
+ */
+ function constant(value) {
+ return function() {
+ return value;
+ };
+ }
+
+ /**
+ * Produces a callback bound to an optional `thisArg`. If `func` is a property
+ * name the created callback will return the property value for a given element.
+ * If `func` is an object the created callback will return `true` for elements
+ * that contain the equivalent object properties, otherwise it will return `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} [func=identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of the created callback.
+ * @param {number} [argCount] The number of arguments the callback accepts.
+ * @returns {Function} Returns a callback function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // wrap to create custom callback shorthands
+ * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+ * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+ * return !match ? func(callback, thisArg) : function(object) {
+ * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+ * };
+ * });
+ *
+ * _.filter(characters, 'age__gt38');
+ * // => [{ 'name': 'fred', 'age': 40 }]
+ */
+ function createCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (func == null || type == 'function') {
+ return baseCreateCallback(func, thisArg, argCount);
+ }
+ // handle "_.pluck" style callback shorthands
+ if (type != 'object') {
+ return property(func);
+ }
+ var props = keys(func),
+ key = props[0],
+ a = func[key];
+
+ // handle "_.where" style callback shorthands
+ if (props.length == 1 && a === a && !isObject(a)) {
+ // fast path the common case of providing an object with a single
+ // property containing a primitive value
+ return function(object) {
+ var b = object[key];
+ return a === b && (a !== 0 || (1 / a == 1 / b));
+ };
+ }
+ return function(object) {
+ var length = props.length,
+ result = false;
+
+ while (length--) {
+ if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+ break;
+ }
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding HTML entities.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles'
+ */
+ function escape(string) {
+ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+ }
+
+ /**
+ * This method returns the first argument provided to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.identity(object) === object;
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
+
+ /**
+ * Adds function properties of a source object to the destination object.
+ * If `object` is a function methods will be added to its prototype as well.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Function|Object} [object=lodash] object The destination object.
+ * @param {Object} source The object of functions to add.
+ * @param {Object} [options] The options object.
+ * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+ * @example
+ *
+ * function capitalize(string) {
+ * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+ * }
+ *
+ * _.mixin({ 'capitalize': capitalize });
+ * _.capitalize('fred');
+ * // => 'Fred'
+ *
+ * _('fred').capitalize().value();
+ * // => 'Fred'
+ *
+ * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+ * _('fred').capitalize();
+ * // => 'Fred'
+ */
+ function mixin(object, source, options) {
+ var chain = true,
+ methodNames = source && functions(source);
+
+ if (!source || (!options && !methodNames.length)) {
+ if (options == null) {
+ options = source;
+ }
+ ctor = lodashWrapper;
+ source = object;
+ object = lodash;
+ methodNames = functions(source);
+ }
+ if (options === false) {
+ chain = false;
+ } else if (isObject(options) && 'chain' in options) {
+ chain = options.chain;
+ }
+ var ctor = object,
+ isFunc = isFunction(ctor);
+
+ forEach(methodNames, function(methodName) {
+ var func = object[methodName] = source[methodName];
+ if (isFunc) {
+ ctor.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ value = this.__wrapped__,
+ args = [value];
+
+ push.apply(args, arguments);
+ var result = func.apply(object, args);
+ if (chain || chainAll) {
+ if (value === result && isObject(result)) {
+ return this;
+ }
+ result = new ctor(result);
+ result.__chain__ = chainAll;
+ }
+ return result;
+ };
+ }
+ });
+ }
+
+ /**
+ * Reverts the '_' variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ context._ = oldDash;
+ return this;
+ }
+
+ /**
+ * A no-operation function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.noop(object) === undefined;
+ * // => true
+ */
+ function noop() {
+ // no operation performed
+ }
+
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var stamp = _.now();
+ * _.defer(function() { console.log(_.now() - stamp); });
+ * // => logs the number of milliseconds it took for the deferred function to be called
+ */
+ var now = isNative(now = Date.now) && now || function() {
+ return new Date().getTime();
+ };
+
+ /**
+ * Converts the given value into an integer of the specified radix.
+ * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+ * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+ *
+ * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+ * implementations. See http://es5.github.io/#E.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} value The value to parse.
+ * @param {number} [radix] The radix used to interpret the value to parse.
+ * @returns {number} Returns the new integer value.
+ * @example
+ *
+ * _.parseInt('08');
+ * // => 8
+ */
+ var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+ // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+ return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+ };
+
+ /**
+ * Creates a "_.pluck" style function, which returns the `key` value of a
+ * given object.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} key The name of the property to retrieve.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'barney', 'age': 36 }
+ * ];
+ *
+ * var getName = _.property('name');
+ *
+ * _.map(characters, getName);
+ * // => ['barney', 'fred']
+ *
+ * _.sortBy(characters, getName);
+ * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
+ */
+ function property(key) {
+ return function(object) {
+ return object[key];
+ };
+ }
+
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is provided a number between `0` and the given number will be
+ * returned. If `floating` is truey or either `min` or `max` are floats a
+ * floating-point number will be returned instead of an integer.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} [min=0] The minimum possible value.
+ * @param {number} [max=1] The maximum possible value.
+ * @param {boolean} [floating=false] Specify returning a floating-point number.
+ * @returns {number} Returns a random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(min, max, floating) {
+ var noMin = min == null,
+ noMax = max == null;
+
+ if (floating == null) {
+ if (typeof min == 'boolean' && noMax) {
+ floating = min;
+ min = 1;
+ }
+ else if (!noMax && typeof max == 'boolean') {
+ floating = max;
+ noMax = true;
+ }
+ }
+ if (noMin && noMax) {
+ max = 1;
+ }
+ min = +min || 0;
+ if (noMax) {
+ max = min;
+ min = 0;
+ } else {
+ max = +max || 0;
+ }
+ if (floating || min % 1 || max % 1) {
+ var rand = nativeRandom();
+ return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+ }
+ return baseRandom(min, max);
+ }
+
+ /**
+ * Resolves the value of property `key` on `object`. If `key` is a function
+ * it will be invoked with the `this` binding of `object` and its result returned,
+ * else the property value is returned. If `object` is falsey then `undefined`
+ * is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object to inspect.
+ * @param {string} key The name of the property to resolve.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = {
+ * 'cheese': 'crumpets',
+ * 'stuff': function() {
+ * return 'nonsense';
+ * }
+ * };
+ *
+ * _.result(object, 'cheese');
+ * // => 'crumpets'
+ *
+ * _.result(object, 'stuff');
+ * // => 'nonsense'
+ */
+ function result(object, key) {
+ if (object) {
+ var value = object[key];
+ return isFunction(value) ? object[key]() : value;
+ }
+ }
+
+ /**
+ * A micro-templating method that handles arbitrary delimiters, preserves
+ * whitespace, and correctly escapes quotes within interpolated code.
+ *
+ * Note: In the development build, `_.template` utilizes sourceURLs for easier
+ * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ *
+ * For more information on precompiling templates see:
+ * https://lodash.com/custom-builds
+ *
+ * For more information on Chrome extension sandboxes see:
+ * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} text The template text.
+ * @param {Object} data The data object used to populate the text.
+ * @param {Object} [options] The options object.
+ * @param {RegExp} [options.escape] The "escape" delimiter.
+ * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+ * @param {Object} [options.imports] An object to import into the template as local variables.
+ * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+ * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [variable] The data object variable name.
+ * @returns {Function|string} Returns a compiled function when no `data` object
+ * is given, else it returns the interpolated text.
+ * @example
+ *
+ * // using the "interpolate" delimiter to create a compiled template
+ * var compiled = _.template('hello <%= name %>');
+ * compiled({ 'name': 'fred' });
+ * // => 'hello fred'
+ *
+ * // using the "escape" delimiter to escape HTML in data property values
+ * _.template('<b><%- value %></b>', { 'value': '<script>' });
+ * // => '<b><script></b>'
+ *
+ * // using the "evaluate" delimiter to generate HTML
+ * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+ * _.template('hello ${ name }', { 'name': 'pebbles' });
+ * // => 'hello pebbles'
+ *
+ * // using the internal `print` function in "evaluate" delimiters
+ * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+ * // => 'hello barney!'
+ *
+ * // using a custom template delimiters
+ * _.templateSettings = {
+ * 'interpolate': /{{([\s\S]+?)}}/g
+ * };
+ *
+ * _.template('hello {{ name }}!', { 'name': 'mustache' });
+ * // => 'hello mustache!'
+ *
+ * // using the `imports` option to import jQuery
+ * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+ * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+ * // => '<li>fred</li><li>barney</li>'
+ *
+ * // using the `sourceURL` option to specify a custom sourceURL for the template
+ * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+ * compiled(data);
+ * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+ *
+ * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+ * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+ * compiled.source;
+ * // => function(data) {
+ * var __t, __p = '', __e = _.escape;
+ * __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+ * return __p;
+ * }
+ *
+ * // using the `source` property to inline compiled templates for meaningful
+ * // line numbers in error messages and a stack trace
+ * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+ * var JST = {\
+ * "main": ' + _.template(mainText).source + '\
+ * };\
+ * ');
+ */
+ function template(text, data, options) {
+ // based on John Resig's `tmpl` implementation
+ // http://ejohn.org/blog/javascript-micro-templating/
+ // and Laura Doktorova's doT.js
+ // https://github.com/olado/doT
+ var settings = lodash.templateSettings;
+ text = String(text || '');
+
+ // avoid missing dependencies when `iteratorTemplate` is not defined
+ options = iteratorTemplate ? defaults({}, options, settings) : settings;
+
+ var imports = iteratorTemplate && defaults({}, options.imports, settings.imports),
+ importsKeys = iteratorTemplate ? keys(imports) : ['_'],
+ importsValues = iteratorTemplate ? values(imports) : [lodash];
+
+ var isEvaluating,
+ index = 0,
+ interpolate = options.interpolate || reNoMatch,
+ source = "__p += '";
+
+ // compile the regexp to match each delimiter
+ var reDelimiters = RegExp(
+ (options.escape || reNoMatch).source + '|' +
+ interpolate.source + '|' +
+ (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+ (options.evaluate || reNoMatch).source + '|$'
+ , 'g');
+
+ text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+ interpolateValue || (interpolateValue = esTemplateValue);
+
+ // escape characters that cannot be included in string literals
+ source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+ // replace delimiters with snippets
+ if (escapeValue) {
+ source += "' +\n__e(" + escapeValue + ") +\n'";
+ }
+ if (evaluateValue) {
+ isEvaluating = true;
+ source += "';\n" + evaluateValue + ";\n__p += '";
+ }
+ if (interpolateValue) {
+ source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+ }
+ index = offset + match.length;
+
+ // the JS engine embedded in Adobe products requires returning the `match`
+ // string in order to produce the correct `offset` value
+ return match;
+ });
+
+ source += "';\n";
+
+ // if `variable` is not specified, wrap a with-statement around the generated
+ // code to add the data object to the top of the scope chain
+ var variable = options.variable,
+ hasVariable = variable;
+
+ if (!hasVariable) {
+ variable = 'obj';
+ source = 'with (' + variable + ') {\n' + source + '\n}\n';
+ }
+ // cleanup code by stripping empty strings
+ source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+ .replace(reEmptyStringMiddle, '$1')
+ .replace(reEmptyStringTrailing, '$1;');
+
+ // frame code as the function body
+ source = 'function(' + variable + ') {\n' +
+ (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
+ "var __t, __p = '', __e = _.escape" +
+ (isEvaluating
+ ? ', __j = Array.prototype.join;\n' +
+ "function print() { __p += __j.call(arguments, '') }\n"
+ : ';\n'
+ ) +
+ source +
+ 'return __p\n}';
+
+ // Use a sourceURL for easier debugging.
+ // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
+
+ try {
+ var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
+ } catch(e) {
+ e.source = source;
+ throw e;
+ }
+ if (data) {
+ return result(data);
+ }
+ // provide the compiled function's source by its `toString` method, in
+ // supported environments, or the `source` property as a convenience for
+ // inlining compiled templates during the build process
+ result.source = source;
+ return result;
+ }
+
+ /**
+ * Executes the callback `n` times, returning an array of the results
+ * of each callback execution. The callback is bound to `thisArg` and invoked
+ * with one argument; (index).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} n The number of times to execute the callback.
+ * @param {Function} callback The function called per iteration.
+ * @param {*} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns an array of the results of each `callback` execution.
+ * @example
+ *
+ * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+ * // => [3, 6, 4]
+ *
+ * _.times(3, function(n) { mage.castSpell(n); });
+ * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+ *
+ * _.times(3, function(n) { this.cast(n); }, mage);
+ * // => also calls `mage.castSpell(n)` three times
+ */
+ function times(n, callback, thisArg) {
+ n = (n = +n) > -1 ? n : 0;
+ var index = -1,
+ result = Array(n);
+
+ callback = baseCreateCallback(callback, thisArg, 1);
+ while (++index < n) {
+ result[index] = callback(index);
+ }
+ return result;
+ }
+
+ /**
+ * The inverse of `_.escape` this method converts the HTML entities
+ * `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding characters.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} string The string to unescape.
+ * @returns {string} Returns the unescaped string.
+ * @example
+ *
+ * _.unescape('Fred, Barney & Pebbles');
+ * // => 'Fred, Barney & Pebbles'
+ */
+ function unescape(string) {
+ return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+ }
+
+ /**
+ * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {string} [prefix] The value to prefix the ID with.
+ * @returns {string} Returns the unique ID.
+ * @example
+ *
+ * _.uniqueId('contact_');
+ * // => 'contact_104'
+ *
+ * _.uniqueId();
+ * // => '105'
+ */
+ function uniqueId(prefix) {
+ var id = ++idCounter;
+ return String(prefix == null ? '' : prefix) + id;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object that wraps the given value with explicit
+ * method chaining enabled.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 },
+ * { 'name': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _.chain(characters)
+ * .sortBy('age')
+ * .map(function(chr) { return chr.name + ' is ' + chr.age; })
+ * .first()
+ * .value();
+ * // => 'pebbles is 1'
+ */
+ function chain(value) {
+ value = new lodashWrapper(value);
+ value.__chain__ = true;
+ return value;
+ }
+
+ /**
+ * Invokes `interceptor` with the `value` as the first argument and then
+ * returns `value`. The purpose of this method is to "tap into" a method
+ * chain in order to perform operations on intermediate results within
+ * the chain.
+ *
+ * @static
+ * @memberOf _
+ * @category Chaining
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3, 4])
+ * .tap(function(array) { array.pop(); })
+ * .reverse()
+ * .value();
+ * // => [3, 2, 1]
+ */
+ function tap(value, interceptor) {
+ interceptor(value);
+ return value;
+ }
+
+ /**
+ * Enables explicit method chaining on the wrapper object.
+ *
+ * @name chain
+ * @memberOf _
+ * @category Chaining
+ * @returns {*} Returns the wrapper object.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
+ * ];
+ *
+ * // without explicit chaining
+ * _(characters).first();
+ * // => { 'name': 'barney', 'age': 36 }
+ *
+ * // with explicit chaining
+ * _(characters).chain()
+ * .first()
+ * .pick('age')
+ * .value();
+ * // => { 'age': 36 }
+ */
+ function wrapperChain() {
+ this.__chain__ = true;
+ return this;
+ }
+
+ /**
+ * Produces the `toString` result of the wrapped value.
+ *
+ * @name toString
+ * @memberOf _
+ * @category Chaining
+ * @returns {string} Returns the string result.
+ * @example
+ *
+ * _([1, 2, 3]).toString();
+ * // => '1,2,3'
+ */
+ function wrapperToString() {
+ return String(this.__wrapped__);
+ }
+
+ /**
+ * Extracts the wrapped value.
+ *
+ * @name valueOf
+ * @memberOf _
+ * @alias value
+ * @category Chaining
+ * @returns {*} Returns the wrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).valueOf();
+ * // => [1, 2, 3]
+ */
+ function wrapperValueOf() {
+ return this.__wrapped__;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return wrapped values when chaining
+ lodash.after = after;
+ lodash.assign = assign;
+ lodash.at = at;
+ lodash.bind = bind;
+ lodash.bindAll = bindAll;
+ lodash.bindKey = bindKey;
+ lodash.chain = chain;
+ lodash.compact = compact;
+ lodash.compose = compose;
+ lodash.constant = constant;
+ lodash.countBy = countBy;
+ lodash.create = create;
+ lodash.createCallback = createCallback;
+ lodash.curry = curry;
+ lodash.debounce = debounce;
+ lodash.defaults = defaults;
+ lodash.defer = defer;
+ lodash.delay = delay;
+ lodash.difference = difference;
+ lodash.filter = filter;
+ lodash.flatten = flatten;
+ lodash.forEach = forEach;
+ lodash.forEachRight = forEachRight;
+ lodash.forIn = forIn;
+ lodash.forInRight = forInRight;
+ lodash.forOwn = forOwn;
+ lodash.forOwnRight = forOwnRight;
+ lodash.functions = functions;
+ lodash.groupBy = groupBy;
+ lodash.indexBy = indexBy;
+ lodash.initial = initial;
+ lodash.intersection = intersection;
+ lodash.invert = invert;
+ lodash.invoke = invoke;
+ lodash.keys = keys;
+ lodash.map = map;
+ lodash.mapValues = mapValues;
+ lodash.max = max;
+ lodash.memoize = memoize;
+ lodash.merge = merge;
+ lodash.min = min;
+ lodash.omit = omit;
+ lodash.once = once;
+ lodash.pairs = pairs;
+ lodash.partial = partial;
+ lodash.partialRight = partialRight;
+ lodash.pick = pick;
+ lodash.pluck = pluck;
+ lodash.property = property;
+ lodash.pull = pull;
+ lodash.range = range;
+ lodash.reject = reject;
+ lodash.remove = remove;
+ lodash.rest = rest;
+ lodash.shuffle = shuffle;
+ lodash.sortBy = sortBy;
+ lodash.tap = tap;
+ lodash.throttle = throttle;
+ lodash.times = times;
+ lodash.toArray = toArray;
+ lodash.transform = transform;
+ lodash.union = union;
+ lodash.uniq = uniq;
+ lodash.values = values;
+ lodash.where = where;
+ lodash.without = without;
+ lodash.wrap = wrap;
+ lodash.xor = xor;
+ lodash.zip = zip;
+ lodash.zipObject = zipObject;
+
+ // add aliases
+ lodash.collect = map;
+ lodash.drop = rest;
+ lodash.each = forEach;
+ lodash.eachRight = forEachRight;
+ lodash.extend = assign;
+ lodash.methods = functions;
+ lodash.object = zipObject;
+ lodash.select = filter;
+ lodash.tail = rest;
+ lodash.unique = uniq;
+ lodash.unzip = zip;
+
+ // add functions to `lodash.prototype`
+ mixin(lodash);
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions that return unwrapped values when chaining
+ lodash.clone = clone;
+ lodash.cloneDeep = cloneDeep;
+ lodash.contains = contains;
+ lodash.escape = escape;
+ lodash.every = every;
+ lodash.find = find;
+ lodash.findIndex = findIndex;
+ lodash.findKey = findKey;
+ lodash.findLast = findLast;
+ lodash.findLastIndex = findLastIndex;
+ lodash.findLastKey = findLastKey;
+ lodash.has = has;
+ lodash.identity = identity;
+ lodash.indexOf = indexOf;
+ lodash.isArguments = isArguments;
+ lodash.isArray = isArray;
+ lodash.isBoolean = isBoolean;
+ lodash.isDate = isDate;
+ lodash.isElement = isElement;
+ lodash.isEmpty = isEmpty;
+ lodash.isEqual = isEqual;
+ lodash.isFinite = isFinite;
+ lodash.isFunction = isFunction;
+ lodash.isNaN = isNaN;
+ lodash.isNull = isNull;
+ lodash.isNumber = isNumber;
+ lodash.isObject = isObject;
+ lodash.isPlainObject = isPlainObject;
+ lodash.isRegExp = isRegExp;
+ lodash.isString = isString;
+ lodash.isUndefined = isUndefined;
+ lodash.lastIndexOf = lastIndexOf;
+ lodash.mixin = mixin;
+ lodash.noConflict = noConflict;
+ lodash.noop = noop;
+ lodash.now = now;
+ lodash.parseInt = parseInt;
+ lodash.random = random;
+ lodash.reduce = reduce;
+ lodash.reduceRight = reduceRight;
+ lodash.result = result;
+ lodash.runInContext = runInContext;
+ lodash.size = size;
+ lodash.some = some;
+ lodash.sortedIndex = sortedIndex;
+ lodash.template = template;
+ lodash.unescape = unescape;
+ lodash.uniqueId = uniqueId;
+
+ // add aliases
+ lodash.all = every;
+ lodash.any = some;
+ lodash.detect = find;
+ lodash.findWhere = find;
+ lodash.foldl = reduce;
+ lodash.foldr = reduceRight;
+ lodash.include = contains;
+ lodash.inject = reduce;
+
+ mixin(function() {
+ var source = {}
+ forOwn(lodash, function(func, methodName) {
+ if (!lodash.prototype[methodName]) {
+ source[methodName] = func;
+ }
+ });
+ return source;
+ }(), false);
+
+ /*--------------------------------------------------------------------------*/
+
+ // add functions capable of returning wrapped and unwrapped values when chaining
+ lodash.first = first;
+ lodash.last = last;
+ lodash.sample = sample;
+
+ // add aliases
+ lodash.take = first;
+ lodash.head = first;
+
+ forOwn(lodash, function(func, methodName) {
+ var callbackable = methodName !== 'sample';
+ if (!lodash.prototype[methodName]) {
+ lodash.prototype[methodName]= function(n, guard) {
+ var chainAll = this.__chain__,
+ result = func(this.__wrapped__, n, guard);
+
+ return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
+ ? result
+ : new lodashWrapper(result, chainAll);
+ };
+ }
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf _
+ * @type string
+ */
+ lodash.VERSION = '2.4.2';
+
+ // add "Chaining" functions to the wrapper
+ lodash.prototype.chain = wrapperChain;
+ lodash.prototype.toString = wrapperToString;
+ lodash.prototype.value = wrapperValueOf;
+ lodash.prototype.valueOf = wrapperValueOf;
+
+ // add `Array` functions that return unwrapped values
+ baseEach(['join', 'pop', 'shift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ result = func.apply(this.__wrapped__, arguments);
+
+ return chainAll
+ ? new lodashWrapper(result, chainAll)
+ : result;
+ };
+ });
+
+ // add `Array` functions that return the existing wrapped value
+ baseEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ func.apply(this.__wrapped__, arguments);
+ return this;
+ };
+ });
+
+ // add `Array` functions that return new wrapped values
+ baseEach(['concat', 'slice', 'splice'], function(methodName) {
+ var func = arrayRef[methodName];
+ lodash.prototype[methodName] = function() {
+ return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
+ };
+ });
+
+ // avoid array-like object bugs with `Array#shift` and `Array#splice`
+ // in IE < 9, Firefox < 10, Narwhal, and RingoJS
+ if (!support.spliceObjects) {
+ baseEach(['pop', 'shift', 'splice'], function(methodName) {
+ var func = arrayRef[methodName],
+ isSplice = methodName == 'splice';
+
+ lodash.prototype[methodName] = function() {
+ var chainAll = this.__chain__,
+ value = this.__wrapped__,
+ result = func.apply(value, arguments);
+
+ if (value.length === 0) {
+ delete value[0];
+ }
+ return (chainAll || isSplice)
+ ? new lodashWrapper(result, chainAll)
+ : result;
+ };
+ });
+ }
+
+ // add pseudo private property to be used and removed during the build process
+ lodash._baseEach = baseEach;
+ lodash._iteratorTemplate = iteratorTemplate;
+ lodash._shimKeys = shimKeys;
+
+ return lodash;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose Lo-Dash
+ var _ = runInContext();
+
+ // some AMD build optimizers like r.js check for condition patterns like the following:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // Expose Lo-Dash to the global object even when an AMD loader is present in
+ // case Lo-Dash is loaded with a RequireJS shim config.
+ // See http://requirejs.org/docs/api.html#config-shim
+ root._ = _;
+
+ // define as an anonymous module so, through path mapping, it can be
+ // referenced as the "underscore" module
+ define(function() {
+ return _;
+ });
+ }
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ else if (freeExports && freeModule) {
+ // in Node.js or RingoJS
+ if (moduleExports) {
+ (freeModule.exports = _)._ = _;
+ }
+ // in Narwhal or Rhino -require
+ else {
+ freeExports._ = _;
+ }
+ }
+ else {
+ // in a browser or Rhino
+ root._ = _;
+ }
+}.call(this));
--- /dev/null
+{
+ "name": "lodash",
+ "version": "2.4.2",
+ "description": "A utility library delivering consistency, customization, performance, & extras.",
+ "homepage": "http://lodash.com/",
+ "license": "MIT",
+ "main": "dist/lodash.js",
+ "keywords": [
+ "amd",
+ "browser",
+ "client",
+ "customize",
+ "functional",
+ "server",
+ "util"
+ ],
+ "author": {
+ "name": "John-David Dalton",
+ "email": "john.david.dalton@gmail.com",
+ "url": "http://allyoucanleet.com/"
+ },
+ "contributors": [
+ {
+ "name": "John-David Dalton",
+ "email": "john.david.dalton@gmail.com",
+ "url": "http://allyoucanleet.com/"
+ },
+ {
+ "name": "Blaine Bublitz",
+ "email": "blaine@iceddev.com",
+ "url": "http://www.iceddev.com/"
+ },
+ {
+ "name": "Kit Cambridge",
+ "email": "github@kitcambridge.be",
+ "url": "http://kitcambridge.be/"
+ },
+ {
+ "name": "Mathias Bynens",
+ "email": "mathias@qiwi.be",
+ "url": "http://mathiasbynens.be/"
+ }
+ ],
+ "bugs": {
+ "url": "https://github.com/lodash/lodash/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/lodash/lodash.git"
+ },
+ "engines": [
+ "node",
+ "rhino"
+ ],
+ "files": [
+ "LICENSE.txt",
+ "lodash.js",
+ "dist/lodash.js",
+ "dist/lodash.min.js",
+ "dist/lodash.compat.js",
+ "dist/lodash.compat.min.js",
+ "dist/lodash.underscore.js",
+ "dist/lodash.underscore.min.js"
+ ],
+ "jam": {
+ "main": "dist/lodash.compat.js",
+ "include": [
+ "LICENSE.txt",
+ "dist/lodash.js",
+ "dist/lodash.min.js",
+ "dist/lodash.compat.js",
+ "dist/lodash.compat.min.js",
+ "dist/lodash.underscore.js",
+ "dist/lodash.underscore.min.js"
+ ]
+ },
+ "volo": {
+ "type": "directory",
+ "ignore": [
+ ".*",
+ "*.custom.*",
+ "*.min.*",
+ "*.template.*",
+ "*.map",
+ "*.md",
+ "lodash.js",
+ "index.js",
+ "bower.json",
+ "component.json",
+ "doc",
+ "modularize",
+ "node_modules",
+ "perf",
+ "test",
+ "vendor"
+ ]
+ },
+ "_id": "lodash@2.4.2",
+ "scripts": {},
+ "_shasum": "fadd834b9683073da179b3eae6d9c0d15053f73e",
+ "_from": "lodash@>=2.4.1 <3.0.0",
+ "_npmVersion": "2.7.5",
+ "_nodeVersion": "0.12.2",
+ "_npmUser": {
+ "name": "jdalton",
+ "email": "john.david.dalton@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "jdalton",
+ "email": "john.david.dalton@gmail.com"
+ },
+ {
+ "name": "mathias",
+ "email": "mathias@qiwi.be"
+ },
+ {
+ "name": "phated",
+ "email": "blaine@iceddev.com"
+ },
+ {
+ "name": "kitcambridge",
+ "email": "github@kitcambridge.be"
+ },
+ {
+ "name": "d10",
+ "email": "demoneaux@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "fadd834b9683073da179b3eae6d9c0d15053f73e",
+ "tarball": "http://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+}
--- /dev/null
+{
+ "name": "grunt-contrib-less",
+ "description": "Compile LESS files to CSS.",
+ "version": "1.0.1",
+ "homepage": "https://github.com/gruntjs/grunt-contrib-less",
+ "author": {
+ "name": "Grunt Team",
+ "url": "http://gruntjs.com/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/gruntjs/grunt-contrib-less.git"
+ },
+ "bugs": {
+ "url": "https://github.com/gruntjs/grunt-contrib-less/issues"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/gruntjs/grunt-contrib-less/blob/master/LICENSE-MIT"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "grunt test"
+ },
+ "dependencies": {
+ "async": "^0.9.0",
+ "chalk": "^0.5.1",
+ "less": "~2.4.0",
+ "lodash": "^2.4.1"
+ },
+ "devDependencies": {
+ "grunt": "^0.4.5",
+ "grunt-contrib-clean": "^0.6.0",
+ "grunt-contrib-internal": "^0.4.11",
+ "grunt-contrib-jshint": "^0.10.0",
+ "grunt-contrib-nodeunit": "^0.4.1",
+ "less-plugin-clean-css": "^1.2.0"
+ },
+ "peerDependencies": {
+ "grunt": "~0.4.5"
+ },
+ "keywords": [
+ "gruntplugin"
+ ],
+ "files": [
+ "tasks",
+ "LICENSE-MIT"
+ ],
+ "appveyor_id": "e3aa4d07xe4w4u05",
+ "contributors": [
+ {
+ "name": "Tyler Kellen",
+ "url": "http://goingslowly.com/"
+ },
+ {
+ "name": "\"Cowboy\" Ben Alman",
+ "url": "http://benalman.com/"
+ },
+ {
+ "name": "Chris Talkington",
+ "url": "http://christalkington.com/"
+ },
+ {
+ "name": "Teddy Cross",
+ "url": "http://tkaz.ec/"
+ },
+ {
+ "name": "Justin Searls",
+ "url": "http://about.me/searls/"
+ },
+ {
+ "name": "Thomas Boyt",
+ "url": "http://www.thomasboyt.com/"
+ },
+ {
+ "name": "Jake Harding",
+ "url": "http://thejakeharding.com/"
+ },
+ {
+ "name": "Jason Karns",
+ "url": "http://jasonkarns.com/"
+ },
+ {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net/"
+ }
+ ],
+ "gitHead": "75e1386ac380c553f5a8b1984f905d991864855c",
+ "_id": "grunt-contrib-less@1.0.1",
+ "_shasum": "87f32a5e474987a405c9c9890d33686a45021d51",
+ "_from": "grunt-contrib-less@*",
+ "_npmVersion": "2.4.1",
+ "_nodeVersion": "0.10.35",
+ "_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": "jmeas",
+ "email": "jellyes2@gmail.com"
+ },
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "87f32a5e474987a405c9c9890d33686a45021d51",
+ "tarball": "http://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-1.0.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-1.0.1.tgz"
+}
--- /dev/null
+/*
+ * grunt-contrib-less
+ * http://gruntjs.com/
+ *
+ * Copyright (c) 2014 Tyler Kellen, contributors
+ * Licensed under the MIT license.
+ */
+
+'use strict';
+
+var path = require('path');
+var _ = require('lodash');
+var async = require('async');
+var chalk = require('chalk');
+var less = require('less');
+
+module.exports = function(grunt) {
+ grunt.registerMultiTask('less', 'Compile LESS files to CSS', function() {
+ var done = this.async();
+
+ var options = this.options({
+ banner: ''
+ });
+
+ if (this.files.length < 1) {
+ grunt.verbose.warn('Destination not written because no source files were provided.');
+ }
+
+ async.eachSeries(this.files, function(f, nextFileObj) {
+ var destFile = f.dest;
+
+ var files = f.src.filter(function(filepath) {
+ // Warn on and remove invalid source files (if nonull was set).
+ if (!grunt.file.exists(filepath)) {
+ grunt.log.warn('Source file "' + filepath + '" not found.');
+ return false;
+ } else {
+ return true;
+ }
+ });
+
+ if (files.length === 0) {
+ if (f.src.length < 1) {
+ grunt.log.warn('Destination ' + chalk.cyan(destFile) + ' not written because no source files were found.');
+ }
+
+ // No src files, goto next target. Warn would have been issued above.
+ return nextFileObj();
+ }
+
+ var compiled = [];
+ var i = 0;
+
+ async.concatSeries(files, function(file, next) {
+ if (i++ > 0) {
+ options.banner = '';
+ }
+
+ compileLess(file, destFile, options)
+ .then(function(output) {
+ compiled.push(output.css);
+ if (options.sourceMap && !options.sourceMapFileInline) {
+ var sourceMapFilename = options.sourceMapFilename;
+ if (!sourceMapFilename) {
+ sourceMapFilename = destFile + '.map';
+ }
+ grunt.file.write(sourceMapFilename, output.map);
+ grunt.log.writeln('File ' + chalk.cyan(options.sourceMapFilename) + ' created.');
+ }
+ process.nextTick(next);
+ },
+ function(err) {
+ nextFileObj(err);
+ });
+ }, function() {
+ if (compiled.length < 1) {
+ grunt.log.warn('Destination ' + chalk.cyan(destFile) + ' not written because compiled files were empty.');
+ } else {
+ var allCss = compiled.join(options.compress ? '' : grunt.util.normalizelf(grunt.util.linefeed));
+ grunt.file.write(destFile, allCss);
+ grunt.log.writeln('File ' + chalk.cyan(destFile) + ' created');
+ }
+ nextFileObj();
+ });
+
+ }, done);
+ });
+
+ var compileLess = function(srcFile, destFile, options) {
+ options = _.assign({filename: srcFile}, options);
+ options.paths = options.paths || [path.dirname(srcFile)];
+
+ if (typeof options.paths === 'function') {
+ try {
+ options.paths = options.paths(srcFile);
+ } catch (e) {
+ grunt.fail.warn(wrapError(e, 'Generating @import paths failed.'));
+ }
+ }
+
+ if (options.sourceMap && !options.sourceMapFilename) {
+ options.sourceMapFilename = destFile + '.map';
+ }
+
+ if (typeof options.sourceMapBasepath === 'function') {
+ try {
+ options.sourceMapBasepath = options.sourceMapBasepath(srcFile);
+ } catch (e) {
+ grunt.fail.warn(wrapError(e, 'Generating sourceMapBasepath failed.'));
+ }
+ }
+
+ if (typeof(options.sourceMap) === "boolean" && options.sourceMap) {
+ options.sourceMap = {
+ sourceMapBasepath: options.sourceMapBasepath,
+ sourceMapFilename: options.sourceMapFilename,
+ sourceMapInputFilename: options.sourceMapInputFilename,
+ sourceMapFullFilename: options.sourceMapFullFilename,
+ sourceMapURL: options.sourceMapURL,
+ sourceMapRootpath: options.sourceMapRootpath,
+ outputSourceFiles: options.outputSourceFiles,
+ sourceMapFileInline: options.sourceMapFileInline
+ };
+ }
+
+ var srcCode = grunt.file.read(srcFile);
+
+ // Equivalent to --modify-vars option.
+ // Properties under options.modifyVars are appended as less variables
+ // to override global variables.
+ var modifyVarsOutput = parseVariableOptions(options['modifyVars']);
+ if (modifyVarsOutput) {
+ srcCode += '\n';
+ srcCode += modifyVarsOutput;
+ }
+
+ // Load custom functions
+ if (options.customFunctions) {
+ Object.keys(options.customFunctions).forEach(function(name) {
+ less.functions.functionRegistry.add(name.toLowerCase(), function() {
+ var args = [].slice.call(arguments);
+ args.unshift(less);
+ var res = options.customFunctions[name].apply(this, args);
+ return typeof res === 'object' ? res : new less.tree.Anonymous(res);
+ });
+ });
+ }
+
+ return less.render(srcCode, options)
+ .catch(function(err) {
+ lessError(err, srcFile);
+ });
+ };
+
+ var parseVariableOptions = function(options) {
+ var pairs = _.pairs(options);
+ var output = '';
+ pairs.forEach(function(pair) {
+ output += '@' + pair[0] + ':' + pair[1] + ';';
+ });
+ return output;
+ };
+
+ var formatLessError = function(e) {
+ var pos = '[' + 'L' + e.line + ':' + ('C' + e.column) + ']';
+ return e.filename + ': ' + pos + ' ' + e.message;
+ };
+
+ var lessError = function(e, file) {
+ var message = less.formatError ? less.formatError(e) : formatLessError(e);
+
+ grunt.log.error(message);
+ grunt.fail.warn('Error compiling ' + file);
+ };
+
+ var wrapError = function (e, message) {
+ var err = new Error(message);
+ err.origError = e;
+ return err;
+ };
+};
"grunt-contrib-watch": "^0.6.1"
},
"devDependencies": {
- "grunt-concat-in-order": "^0.1.6"
+ "grunt-concat-in-order": "^0.1.6",
+ "grunt-contrib-less": "^1.0.1"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
background: #f9f9fa;
}
-.text {
+.todo > .text {
font-size: 20pt;
margin-top: 5px;
margin-bottom: 5px;
display: none;
}
-#shortcuts .editing {
- color: red;
- text-color: red;
- border: 2px solid;
-}
#todo-list {
width: 80%;
background: #ffffff;
}
+.with-dropshadow {
+ box-shadow: 0 2px 12px rgba(0,0,0,0.6);
+}
+
#shortcuts-wrapper {
position: fixed;
bottom: -2px;
left: 10px;
width: 200px;
background: #ffffff;
+ .with-dropshadow;
+ .title {
+ padding: 10px;
+ font-size: 100%;
+ border-bottom: 1px solid #979797;
+ font-weight: bold;
+ background: -webkit-gradient( linear, left bottom, left top, color-stop(1, rgb(237,237,237)), color-stop(0, rgb(196,196,196)) );
+ }
}
-
.shortcuts {
border-collapse: collapse;
- box-shadow: 0 2px 12px rgba(0,0,0,0.6);
display: block;
-}
-
-.shortcuts .description {
- background-color: #efefef;
- max-width: 80px;
- padding: 0 0 0 7px;
-}
-
-.shortcuts .keybinding {
- padding: 0 0 0 7px;
-}
-
-.shortcuts td {
- border-top: 0;
- border-bottom: 1px solid #aaa;
-}
-
-#shortcuts-wrapper .title {
- padding: 10px;
- font-size: 100%;
- border-bottom: 1px solid #979797
- font-weight: bold;
- background: -webkit-gradient( linear, left bottom, left top, color-stop(1, rgb(237,237,237)), color-stop(0, rgb(196,196,196)) );
+ width: 100%;
+ .description {
+ background-color: #efefef;
+ max-width: 80px;
+ padding: 0 0 0 7px;
+ }
+ .keybinding {
+ padding: 0 0 0 7px;
+ }
+ td {
+ border-top: 0;
+ border-bottom: 1px solid #aaa;
+ }
+ .editing {
+ color: red;
+ text-color: red;
+ }
}
#reset-button {